Java Volatile关键字
Java Volatile关键字
volatile是java虚拟机提供的一种轻量级同步机制
volatile有三大特性
- 保证可见性
- 不保证原子性
- 禁止指令重排
保证可见性
即线程在自己工作内存产生的变化在其他线程中是可见的。当线程改变值的时候,cpu会通知其他线程,该值失效,请重新从主存中读取。
简单的理解就是大家都在主存中读写
class MyData1 {
int num = 0;
public void add() {
this.num = 1;
}
}
class MyData2 {
//使用volatile修饰
volatile int num = 0;
public void add() {
this.num = 1;
}
}
/**
* volatile可见性测试demo
*/
public class VolatileTest {
public static void main(String[] args) {
//新建两个内部类
MyData1 myData = new MyData1();
MyData2 myData2 = new MyData2();
//这里已经知道结果了,所以直接test2先执行
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "开始执行");
try {
//这里睡一秒,等主线程拿到mun
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
myData2.add();
}, "test2").start();
//循环判断值变化
while (myData2.num == 0) {
}
System.out.println("mun2不等于0");
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "开始执行");
try {
//这里睡一秒,等主线程拿到mun
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
myData.add();
}, "test1").start();
while (myData.num == 0) {
}
System.out.println("mun不等于0");
}
}
test2开始执行
mun2不等于0
test1开始执行
不保证原子性
原子性:不可分割,完整性,即某个业务要么同时成功,要么同时失败
如下demo,本应该输出20000,实际输出19673,并不能保证原子性
class MyData1 {
volatile int num = 0;
public void add() {
num++;
}
}
/**
* volatile原子性测试demo
*/
public class VolatileTest {
public static void main(String[] args) {
MyData1 mydata = new MyData1();
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
mydata.add();
}
},"aa").start();
}
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(mydata.num);
}
}
19673
禁止指令重排
计算机在执行程序的时候,为了提高性能,编译器和处理器会对指令做重排
oa=>operation: 源代码
ob=>operation: 编译器优化重排
oc=>operation: 指令并行的重排
od=>operation: 内存系统的重排
oe=>operation: 最终执行的指令
oa(right)->ob(right)->oc(right)->od(right)->oe
即:单线程环境里面可以确保程序最终执行结果一致。
处理器在进行重排序时必须考虑指令之间的数据依赖性(即现有鸡再有蛋)。
多线程环境中线程交替执行,由于编译器优化排序的存在,两个线程中的变更能否保证一致性是无法确定的,结果无法预测。
int a = 1;
int b = 2;
a++;
b=a+1;
//这些指令可以按以下顺序重排,而不改变程序的语义
int a = 1;
a++;
int b = 2;
b=a+1;
//
int b = 2;
int a = 1;
a++;
b=a+1;
最经典的应用就是双端检索懒加载的单例模式
双端检索机制不能100%保证指令执行的顺序,可能百分之99是正确的,但是发生指令重排,可能就会导致出错

浙公网安备 33010602011771号