线程同步: volatile
并发可见性问题
可见性:线程对主内存的修改可以及时的被其他线程观察到
执行结果:
上面的程序中,线程2的虽然将共享变量stop设置为了true,但是线程1仍然继续执行,确实存在不可见性的问题。
线程之间不可见
线程之间的不可见是有Java内存模型决定的:
Java所有变量都存储在主内存中
每个线程都有自己独立的工作空间
每个线程的工作空间中,存储了使用到的共享变量的副本
线程对共享变量的操作,都会在自己的工作内存中进行,不能直接在主内存中读写
不同线程之间,无法访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成
volatile保证可见性
上述程序如果想要保证可见性,只需要给变量 stop
添加关键字 volatile
即可,volatile的意思是可变的、易变的,指代此变量会发生变化:
volatile 保证可见性是通过store与load指令完成的;也就是对volatile变量执行写操作时,会在写操作后加入一条store指令,即强迫线程将最新的值刷新到主内存中;而在读操作时,会加入一条load指令,即强迫从主内存中读入变量的值。
volatile不保证volatile变量的原子性
synchronized保证可见性
注意,synchronized也可以保证可见性: 在JMM中,synchronized规定,线程在加锁时, 先清空工作内存→在主内存中拷贝最新变量的副本到工作内存→执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁
。
CPU MESI
CPU缓存一致性协议。
指令重排序
CPU在执行指令前,为了提高执行效率,会进行指令重排序,而volatile关键字将会禁止指令重排序。
单例模式-双重检查锁
上述INSTANCE变量没有增加 volatile,在超高并发下有可能因指令重排序发生问题。以 Object o = new Object();
为例,转换的指令如下:
由于现代CPU大多采用流水线式的执行方式,JVM也有类似的指令重排序,所以指令执行的顺序并不一定按着从上至下的顺序执行,所以上述指令执行可能变为:
所以判断 InSTANCE == null
位false,retrun了一个 申请内存后给的默认值
,而程序期待的是一个初始化完成的Object对象。
读屏障、写屏障
CPU支持两种原语,在这两种原语中的所有指令不可进行重排序:
loadfence
storefence
最后更新于
这有帮助吗?