volatile关键字

1. 保证线程可见性

在Java内存模型(JMM)中规定,数据存储在主存中,当线程需要数据时,拷贝一份到自己的工作内存中,进行操作,结束后写回主存。以上规定都是理论模型,不一定对应真实实现,但其中的逻辑是一致的。由于都是拿到的拷贝,所以线程之间的修改可能会产生并发问题。
volatile正好就是解决这个问题的关键。volatile修饰的引用,在内容修改后,会通知其他含有该数据的线程他们手中的数据已过期。其他线程就会重新取新的数据。

2. 不保证原子性

volatile无法保证原子性的含义主要是指无法保证诸如i++这样的操作的原子性。
因为Num++可以分为:读取Num的值,将Num的值+1,写入最新的Num的值
三个步骤的话就无法保证这整个操作的原子性了。

3. 禁止指令重排

一般情况下,为了高效的执行指令,指令重排是会发生的,重排的一个准则就是保证单线程的情况下执行结果不变。然而在多线程的情况下就容易产生一些问题。

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

上述代码是双重校验的单例模式。
如果不加volatile修饰,就可能产生问题,产生问题的是这一行

singleton = new Singleton();

我们知道new一个对象的主要逻辑分为三步

  1. 开辟一块内存
  2. 初始化对象
  3. 设置引用指向内存。

在单线程的情况下,不管是123还是132的执行顺序都不会产生错误,意味着可能会发生指令重排变成132。
多线程的情况下,某个线程先生成了对象,执行顺序为132,在执行完第3步的时候,已经有对象指向了内存空间,所以

singleton != null

如果此时执行别的线程发现不为空,进行操作的时候就会发现错误,因为还未初始化。

你可能感兴趣的:(学习感悟)