浅谈Java三种实现线程同步的方法

Posted main(0)

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈Java三种实现线程同步的方法相关的知识,希望对你有一定的参考价值。

浅谈Java三种实现线程同步的方法

一、概述

无论是什么语言,在多线程编程中,常常会遇到多个线同时操作程某个变量(读/写),如果读/写不同步,则会造成不符合预期的结果。

例如:线程A和线程B并发运行,都操作变量X,若线程A对变量X进行赋上一个新值,线程B仍然使用变量X之前的值,很明显线程B使用的X不是我们想要的值了。

Java提供了三种机制,解决上述问题,实现线程同步:

  • 同步代码块

    synchronized(锁对象){
    	// 这里添加受保护的数据操作
    }
    
  • 同步方法

    • 静态同步方法:synchronized修饰的静态方法,它的同步锁是当前方法所在类的字节码对象
    public static synchronized void staticMethod(){
        
    }
    
    • 非静态同步方法:synchronized修饰的非静态方法,它的同步锁即为this
    public synchronize void method(){
    
    }
    
  • 锁机制

    // 以可重入锁举例
    Lock lock = new ReentrantLock(/*fail*/);  
    // fail: 
    // true表示使用公平锁,即线程等待拿到锁的时间越久,越容易拿到锁
    // false表示使用非公平锁,线程拿到锁全靠运气。。。cpu时间片轮到哪个线程,哪个线程就能获取锁
    lock.lock();
    // 这里添加受保护的数据操作
    lock.unlock();
    

个人理解:其实无论哪种机制实现线程同步,本质上都是加锁->操作数据->解锁的过程。同步代码块是针对{}中,同步方法是针对整个方法。其ReentrantLock类提供的lock和unlock和C++的std::mutex提供lock和unlock类似

二、测试用例

image-20200531205114538

  1. 同步代码块测试类

    package base.synchronize;
    
    
    public class SynchronizeBlock implements Runnable {
        private int num = 100;
    
        @Override
        public void run() {
            while (num > 1) {
                synchronized (this) {
                    // 同步代码块,只有拿到锁,才有cpu执行权
                    System.out.println("Thread ID:" +  Thread.currentThread().getId() + "---num:" + num);
                    num--;
                }
            }
            System.out.println("Thread ID:" +  Thread.currentThread().getId() + " exit");
        }
    }
    
    
  2. 同步方法测试类

    package base.synchronize;
    
    public class SynchronizeMethod implements Runnable {
        private int num = 100;
        public static int staticNum = 100;
        boolean useStaticMethod;
    
        public SynchronizeMethod(boolean useStaticMethodToTest) {
            this.useStaticMethod = useStaticMethodToTest;
        }
    
        // 对于非静态方法,同步锁对象即this
        public synchronized void method() {
            System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
            num--;
        }
    
        // 对于静态方法,同步锁对象是当前方法所在类的字节码对象
        public synchronized static void staticMethod() {
            System.out.println("Static Method Thread ID:" + Thread.currentThread().getId() + "---num:" + staticNum);
            staticNum--;
        }
    
        @Override
        public void run() {
            if (useStaticMethod) {  // 测试静态同步方法
                while (staticNum > 1) {
                    staticMethod();
                }
            }else{  // 测试非静态同步方法
                while (num > 1){
                    method();
                }
            }
            System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
        }
    }
    
  3. ReentrantLock测试类

    package base.synchronize;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class SynchronizeLock implements Runnable {
        private Lock lock = null;
        private int num = 100;
    
        public SynchronizeLock(boolean fair){
            lock = new ReentrantLock(fair); // 可重入锁
        }
    
        @Override
        public void run() {
            while (num > 1) {
                try {
                    lock.lock();
                    System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
                    num--;
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
            System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
        }
    }
    
    

测试三种机制的Demo

package base.synchronize;

public class Demo {
    public static void main(String[] args) {
        synchronizeBlockTest();     // 同步代码块
        synchronizeMethodTest();    // 同步非静态方法
        synchronizeStaticMethodTest();  // 同步静态方法
        synchronizeLockTest();    // 可重入锁机制
    }

    public static void synchronizeBlockTest(){
        Runnable run = new SynchronizeBlock();
        for(int i = 0; i < 3; i++){
            new Thread(run).start();
        }
    }

    public static void synchronizeMethodTest(){
        Runnable run = new SynchronizeMethod(false);
        for(int i = 0; i < 3; i++){
            new Thread(run).start();
        }

    }
    public static void synchronizeStaticMethodTest() {
        Runnable run = new SynchronizeMethod(true);
        for(int i = 0; i < 3; i++){
            new Thread(run).start();
        }
    }


    public static void synchronizeLockTest(){
        Runnable run = new SynchronizeLock(false);  // true:使用公平锁  false:使用非公平锁
        for(int i = 0; i < 3; i++){
            new Thread(run).start();
        }
    }
}

无论哪种机制,都得到预期的效果,打印100-0

以上是关于浅谈Java三种实现线程同步的方法的主要内容,如果未能解决你的问题,请参考以下文章

JAVA之线程同步的三种方法

JAVA笔记(19)--- 线程概述;如何实现多线程并发;线程生命周期;Thread常用方法;终止线程的三种方式;线程安全问题;synchronized 实现同步线程模型;

Java创建线程的三种方式,同步锁的实现synchronized,lock

浅谈Java线程安全

浅谈集合之线程安全方案

面经总结:多线程