天天看点

Java单例的由浅入深----懒汉式的升级(2 原子操作、指令重排)懒汉式的最终版本(volatile)

知识点:什么是原子操作

简单的来说,原子操作(atomic)就是不可分割的操作,在计算机中,就是指不会因为线程调度被打断的操作。

比如,简单的赋值就是一个原子操作

例如m原先的值为0,那么对于这个操作,要么执行成功变成了6,要么执行失败变成了0,而不会出现诸如m=3这种中间状态–即使是在并发的线程中。

然而声明赋值就不是一个原子操作:

对于这个语句,至少有两个操作:

1、声明一个变量m

2、给m赋值为6

这样就会有一个中间状态:变量m已经被申明了但是还没有赋值

所以,这种状况在多线程中,由于线程执行的不确定性,如果两个线程都使用m,就可能倒是不稳定的结果出现

知识点:什么是指令重排

简单的来说:就是计算机为了提高执行效率,会做一些优化,在不影响最终结果的情况下,可能会对一些语句的执行顺序进行调整。

比如:

int a;//语句1
a=;//语句2
int b =; //语句3
int c =a+b;//语句4
           

正常来说,对于顺序结构,执行的顺序是自上到下

但是,由于指令重排,因为不影响最终结果,所以执行的顺序很可能编程3124,或者1234

由于3,4没有原子性问题,语句3,4可能会被拆分成原子操作,再重排。

也就是说,对于非原子性操作,在不影响结果的情况下,其拆分成的原子操作可能会重排执行顺序

所以在博客1中的懒汉式代码中 singleton = new Singleton()这句,这并非是一个原子操作,事实上在JVM中这句话大概做了下面3件事情

1.给singleton分配内存

2.调用Singleton的构造函数来初始化成员变量

3.将singleton对象指向分配的内存空间(执行到这一步,singleton才是非null的了)

但是在JVM的及时编译器中,存在指令重拍的优化,也就是说,第二步和第三步的顺序是无法保证的

而导致程序出错

所以我们拥有了一个最终版本

3.4终极版本:volatile

public class Singleton {
    private static volatile Singleton singleton;

    private Singleton() {
    }
    public static Singleton getsingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}
           

volatile关键字的作用是禁止指令重排,把singleton声明为volatile之后,对它的写操作就会有一个内存屏障,这样,在它完成赋值之前,就不会调用读的操作了