volatile
volatile
多线程下的变量不可见性:多个线程修改共享的成员变量,会出现一个线程修改了共享的变量值以后,另一个线程不能直接看到该线程修改后的变量的最新值
volatile修饰关键字原理
volatile修改的变量可以在多线程并发修改下,实现线程间变量的可见性
volatile不保证原子性(原子性指在一次操作或者多次操作中,要么所有的操作全部得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行)
AtomicInteger count =new AtomicInteger(1);//该原子类对象在不加锁的前期下实现线程的安全运行
指令禁止重排序:volatile可以防止重排序操作
重排序:为了提高性能编译器和处理器常常会对既定的代码执行顺序进行指令从排序
重排序出现的问题:代码出现的结果有误 例如:
禁止重排序:volatile修饰变量后可以实现禁止指令从排序
volatile内存语义
happens-before:阐述内存之间的内存可见性,如果一个操作执行的结果需要另一个操作可见,那么这两个操作之间必须存在happens-before关系
规则:
- 程序顺序规则: 解释,一个线程中的每个操作,happens-before于该线程中的任意后续操作(
- 锁定规则:解释:对于一个锁的解锁,happens-before于随后对这个锁的加锁
- volatile:解释:对于一个volatile域的写,happens-before于任意后续对这个volatile域的读
- 传递性:解释:如果A happen-before B且 B happen-before C那么A happens-before C
- 启动规则:Thread对象的start()方法先行发生于此线程的每一个动作
- 中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检查到中断事件的发生
- 终止规则:线程中得所有操作都先行发生于对此线程的终止检测
- 终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始
volatile在双重检查加锁的单例中的应用:
饿汉式单例:在获取单例对象之前对象就创建完成了
懒汉式单例:在真正需要单例的时候才创建出该对象(线程不安全,在进行对象创建时当两个线程同时访问时,可能实例返回都为空)
懒汉式(volatile双重检查模式):
public class Instance {
/**
*双重检查机制,以及使用volatile修饰
* 步骤:1.构造器私有
* 2.提供一个静态变量用于存储一个单例对象
* 3.提供方法进行双重检查机制返回单例对象
* 4.使用volatile修饰静态的变量
* 加volatile关键字的目的在于防止指令从排序
*/
private volatile static Instance instance;
private Instance(){}
public static Instance getInstance() {
if (instance==null){
synchronized (Instance.class){
//第二次检查,判断单例对象的变量是否为空
if (instance==null){
instance=new Instance();
}}}
return instance;
}
}
静态内部类单例:
public class Instance {
private static class singleton {
private static final Instance INSTANCE=new Instance();
}
public static Instance getInstance(){
return singleton.INSTANCE;
}
}
注:静态内部类是在被调用是才会被加载,加上JVM的特性,这种方式实现了线程安全的创建单例对象
对比基于volatile的双重检查锁定方案和初始化类的对比,基于类初始化的实现更加简洁,但volatile的额外优势有:除了可以对静态字段实现延迟加载初始化外,还可以对实例字段实现延迟初始化
使用场景:
1.volatile适合做多线程中的纯赋值操作,如果一个共享自变量始终只被多个线程赋值,而没有其他的操作,那么可以使用volatile来代替synchronize或者代替原子变量,因为赋值自身是有原子性的,而volatile又保证可见性,所以足够保证线程安全
2.按照volatile的可见性和禁止重排序以及happens-before规则,volatile可以作为刷新之前变量的触发器,我们可以将某个变量设置为volatile修饰,其他线程一旦发现该变量修改的值后,触发获取到的该变量之前的操作都将是最新的且可见
volatile与synchronize区别
- volatile只能修饰实例变量和类变量,而synchronize可以修饰方法以及代码块
- volatile保证数据的可见性,但不保证原子性,而synchronize是一种排他的机制
- volatile用于禁止指令从排序:可以解决单例双重检查对象初始化代码执行乱序问题
- volatile可以看做事轻量版的synchronize,volatile不保证原子性,但是如果对一个共享变量进行多个线程的赋值,而没有其他的操作,那么就可以用volatile来代替synchronize因为赋值本身是有原子性的而volatile又保证了可见性,所以就可以保证线程安全了
volatile不保证原子性,但是如果对一个共享变量进行多个线程的赋值,而没有其他的操作,那么就可以用volatile来代替synchronize因为赋值本身是有原子性的而volatile又保证了可见性,所以就可以保证线程安全了