volatile 关键字
volatile修饰变量:
指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。
volatile
关键字 除了防止 JVM 的指令重排 ,还有一个重要的作用就是保证变量的可见性。
并发编程的3个重要特性:
- 原子性 : 一个的操作或者多次操作,要么所有的操作全部都得到执行并且不会收到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。
synchronized
可以保证代码片段的原子性。 - 可见性 :当一个变量对共享变量进行了修改,那么另外的线程都是立即可以看到修改后的最新值。
volatile
关键字可以保证共享变量的可见性。 - 有序性 :代码在执行的过程中的先后顺序,Java 在编译器以及运行期间的优化,代码的执行顺序未必就是编写代码时候的顺序。
volatile
关键字可以禁止指令进行重排序优化。
synchronize
关键字和volatile
关键字的区别:
synchronized
关键字和 volatile
关键字是两个互补的存在,而不是对立的存在!
-
volatile 关键字是线程同步的轻量级实现,所以volatile 性能肯定比 synchronized 关键字要好。但是volatile 关键字只能用于变量
而 synchronized 关键字可以修饰方法以及代码块。
-
volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
-
volatile 关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。
1、volatile是java虚拟机提供的轻量级的同步机制
- 可见性,(共享内存中的变量值发生改变后,通知使用此变量的线程私有栈修改值。称为可见性)
- 不保证原子性、
- 禁止指令重排(java编译器的优化)(有序性)从而避免多线程环境下程序出现乱序执行的现象
计算机在执行程序时,为了提高性能,编译器和处理器常常会做指令重排,一把分为以下3种
(1)单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致.
(2)处理器在进行重新排序是必须要考虑指令之间的数据依赖性(3)多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程使用的变量能否保持一致性是无法确定的,结果无法预测
2、JMM(java内存模型)
JMM(Java Memory Model)本身是一种抽象的概念,并不真实存在,他描述的时一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。
JMM关于同步的规定:
- 线程解锁前,必须把共享变量的值刷新回主内存
- 线程加锁前,必须读取主内存的最新值到自己的工作内存
- 加锁解锁时同一把锁
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有的成为栈空间),工作内存是每个线程的私有数据区域,而java内存模型中规定所有变量都存储在**主内存,主内存是贡献内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先概要将变量从主内存拷贝到自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存的变量副本拷贝**,因此不同的线程件无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,期间要访问过程如下图:
- 可见性
- 原子性
- 有序性
实际应用
/**
*单例模式
*利用volatile的有序性禁止指令重排,避免出现因为指令重排,而造成实例化对象为null的问题
*/
public class SingletonDemo {
private static volatile SingletonDemo instance=null;
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 构造方法");
}
/**
* 双重检测机制
* @return
*/
public static SingletonDemo getInstance(){
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 1; i <=10; i++) {
new Thread(() ->{
SingletonDemo.getInstance();
},String.valueOf(i)).start();
}
}
}