Volitale
原理
可见性
可见性(visibility):是指一个线程对共享变量进行修改,另一个线程立即得到修改后的最新值
写屏障保证在改屏障之前的,对共享变量的改动都同步到主存中
读屏障保证该屏障之后,对共享变量的读取,加载的是最新的数据
class Main{
volatile boolean ready;
//写屏障
public void a1(){
ready=true;
}
//读屏障
public void a2(){
if(ready){
System.out.println("true");
}
}
}
有序性
有序性(Ordering):是指程序员中代码执行的顺序,就是程序最终的运行顺序
写屏障能保证写屏障之前的代码,不会进行指令重排序到后面
读屏障能保证读屏障之后的代码不会进行指令重排序到前面
提示
- volitale只能保证可见性和有序性,并不能保证原子性(synchronized可以做到)
- 有序性也是只保证了本线程内的代码不会被重排序
CAS
cas 比较并交换可以实现无锁的并发
其实底层代码的实现是用的乐观锁
volatile
获取共享变量时,为了保证该变量的可见性,需要用volatile修饰
它可以用来修饰成员变量和静态成员变量,它可以避免线程从自己的工作缓存中查找变量的值,必须到驻村中获取它的值,线程操作volatile变量都是直接操作主存.即一个线程对volatile变量的修改,对另一个线程是可见的.
注意
volatile仅仅是保证了共享变量的可见性,让其他线程能够看到最新值,但不能解决指令交错的问题(不确保原子性)
cas必须借助volatile才能读取到共享变量的最新值来实现比较并交换的效果
AtomicInteger中的value属性是这样定义的
private vlatile int value;
DCL问题
著名的doble-checked locking单例模式为例 (双检查的锁模式1.5之后才可以)
class Main{
private static Main main;
private Main(){};
public static Main getMain() {
//在synchronized外面加一个判断是防止代码每次都进入synchronized保证效率
if (main==null){
synchronized (Main.class){
if (main==null){
main = new Main();
}
}
}
return main;
}
}
其实在多线程环境下上面代码是有问题的
main = new Main();不是一个原子性操作,做了以下的操作
- 创建一个对象,将对象引用入栈
- 复制一份对象的引用
- 利用对象的引用调用构造方法
- 利用对象的引用赋值给static main
也许jvm会优化为,先将对象的引用赋值,再调用构造方法
synchronized只有完全保护一个变量,才能解决原子性,可见性,有序性问题,而上面的例子没有完全保护该变量
解决方法
class Main{
private volatile static Main main;//加上volatile
private Main(){};
public static Main getMain() {
//在synchronized外面加一个判断是防止代码每次都进入synchronized保证效率
if (main==null){
synchronized (Main.class){
if (main==null){
main = new Main();
}
}
}
return main;
}
}
对volitale读取操作之前会加上读屏障,而在volatile写操作之后会加上写屏障.
读屏障能保证读屏障之前的代码跑到后面去
写屏障保证写屏障之后的代码跑到前面去