volatile关键字和java内存模JMM

为了深入了解并行机制,java内存模型JMM是很有必要了解的。JMM的关键技术点都是围绕着多线程的原子性、可见性和有序性来建立的。

原子性:是指一个操作不可中断。在多个线程执行时,一个操作一旦开始,就不会被其他线程干扰。
可见性:是指当一个线程修改了某一个共享变量的值时,其他线程是否可以立即知道这个修改。
有序性:对于一个程序而言,总是习惯认为代码从前往后依次执行的。但是在并发时,程序执行可能会出现乱序,程序在执行时,可能会出现指令重排,重排后的指令与原指令的顺序未必一致。

tips:指令重排序有一个前提,就是保证串行语义一致。
指令重排序的主要原因就是,为了充分利用CPU,利用流水线技术可以使CPU高效执行

Happen-Before规则:
java虚拟机和执行系统会对指令进行一定的重排,但是重排也是有原则的,这些原则是指令重排不可违背的:

  • 程序顺序原则:一个线程内保证语义的串行性。
  • volatile规则:volatile变量的写要先于读发生,这也就保证volatile变量的可见性。
  • 锁规则:解锁必然发生在随后的加锁前。
  • 传递性:A先于B,B先于C,那么A必然先于C。
  • 线程的start()方法先于它的每一个动作。
  • 线程的所有操作先于线程的终结。
  • 线程的中断先于被中断的代码。
  • 对象的构造函数的执行、结束先于finalize()方法。

volatile关键字
查询英文字典,你会发现volatile关键字的含义是,易变得、不稳定的。当你使用volatile关键字声明一个变量时,就等于告诉虚拟机,这个变量极有可能被某一个线程修改。为了确保变量被修改后,所有线程可以看到改动,虚拟机会采取一些措施,保证变量的可见性等特点。
关键字volatile对于保证操作的原子性是有很大帮助,但是volatile不能代替锁,也无法保证复合操作的原子性。
关键字volatile是可以保证数据的可见性额有序性。

双重校验锁中volatile的使用

public class Singleton {
    private volatile static Singleton Singleton;

    private Singleton() {
    }

    public Singleton getInstance() {
        if (null == Singleton) {
            synchronized (Singleton.class) {
                if (null == Singleton) {
                    Singleton = new Singleton();
                }
            }
        }
        return Singleton;
    }
}

当线程A进入getInstance()方法时,先判断Singleton是否为空,进入synchronized方法块,线程B进入getInstance()方法时,线程A占有锁,此时线程B阻塞,当A返回Singleton实例对象时,退出锁,B获得锁,进入synchronized方法块,此时Singleton不为空,直接返回。

正因为Singleton有volatile修饰,可以实现线程间数据共享,以及指令重排序。使得线程B可以获得Singleton的对象。如果不加volatile,由于指令重排序,在单核或者多核处理器上多个线程间无法及时判断。

你可能感兴趣的:(高并发编程,多线程,并发编程,java)