3

Java并发编程中双重检查锁漏洞

 1 year ago
source link: https://www.jdon.com/62589
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Java并发编程中双重检查锁漏洞


单例模式在实际工作或面试中出现的频率更高,double-checked lock是单例常见实现:

public class SimpleSingleton4 {

    private static SimpleSingleton4 INSTANCE;

    private SimpleSingleton4() {
    }

    public static SimpleSingleton4 getInstance() {
        if (INSTANCE == null) {
            synchronized (SimpleSingleton4.class) {
                if (INSTANCE == null) {
                    INSTANCE = new SimpleSingleton4();
                }
            }
        }
        return INSTANCE;
    }
}

这段代码有问题:

public static SimpleSingleton4 getInstance() {
    if (INSTANCE == null) {//1
        synchronized (SimpleSingleton4.class) {//2
            if (INSTANCE == null) {//3
                INSTANCE = new SimpleSingleton4();//4
            }
        }
    }
    return INSTANCE;//5
}

你希望按1, 2, 3, 4, 金额 5顺序执行。

但是Java虚拟机实际上会做一些优化,重新排列一些代码指令。重排后的顺序可能会变成: 1, 3, 2, 4, 5,这样在多线程的情况下也会创建多个实例。重新排列的代码可能如下所示:

public static SimpleSingleton4 getInstance() {
    if (INSTANCE == null) {//1
       if (INSTANCE == null) {//3
           synchronized (SimpleSingleton4.class) {//2
                INSTANCE = new SimpleSingleton4();//4
            }
        }
    }
    return INSTANCE;//5
}

有什么解决办法?
答:您可以将volatile关键字添加到 的定义中INSTANCE。具体代码如下:

public class SimpleSingleton7 {

    private volatile static SimpleSingleton7 INSTANCE;

    private SimpleSingleton7() {
    }

    public static SimpleSingleton7 getInstance() {
        if (INSTANCE == null) {
            synchronized (SimpleSingleton7.class) {
                if (INSTANCE == null) {
                    INSTANCE = new SimpleSingleton7();
                }
            }
        }
        return INSTANCE;
    }
}

关键字可以保证多线程的volatile可见性但不保证原子性,也可以禁止指令重排序。
双重检查锁的机制不仅保证了线程安全,而且相比直接锁,提高了执行效率,节省了内存空间。

volatile是一个非常好的关键字,它可以保证变量在多线程中的可见性,也可以禁止指令重排,但不能保证原子性
可见性主要体现在:一个线程修改一个变量,另一个线程每次都可以得到该变量的最新值。
使用synchronized关键字保证原子性:

public class VolatileTest {

    public int count = 0;
    //这里使用

volatile没有用,无法保证原子性

    //public volatile int count = 0;

    public synchronized void add() {
        count++;
    }

    public static void main(String[] args) {
        final VolatileTest test = new VolatileTest();
        for (int i = 0; i < 20; i++) {
            new Thread() {
                @Override
                public void run() {
                    for (int j = 0; j < 1000; j++) {
                        test.add();
                    }
                }

                ;
            }.start();
        }
        while (Thread.activeCount() > 2) {
            //Ensure that all previous threads are executed
            Thread.yield();
        }

        System.out.println(test.count);
    }
}

 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK