volatile 详解
volatile是Java虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
JMM(Java内存模型)
JMM本身是一种抽象的概念模型并不真实存在,塔描述的是一组规则或规范,通过这组规范定义了程序中各个变量(实例字段、静态字段、构成数组对象的元素)的访问方式
- 线程解锁前,必须把共享变量的值刷新回主内存
- 线程加锁前,必须读取主内存的最新值到自己的工作内存
- 加锁解锁是同一把锁
可见性验证
class MyData {
int number;
public void setNumber60() {
number = 60;
}
}
/**
* 验证volatile可见性
*
* 假如int number = 0; 没有添加volatile关键字
*/
public class VisiblenessTest {
public static void main(String[] args) {
// 资源类
MyData myData = new MyData();
// 线程A
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " come in");
// 模拟操作时间 3S
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
myData.setNumber60();
System.out.println(Thread.currentThread().getName() + " updated number value " + myData.number);
}, "A").start();
// 线程B
new Thread(() -> {
while (myData.number == 0){}
System.out.println(Thread.currentThread().getName() + " is over");
}, "B").start();
}
}
输出:
A come in
A updated number value 60
但程序不结束
解析:
A线程、B线程使用同一份数据
A线程操作后,number已经被改变为60
B线程并不知道number已经被改变,所以一直在循环
使用volatile修饰number后
class MyData {
volatile int number;
public void setNumber60() {
number = 60;
}
}
输出:
A come in
A updated number value 60
B is over
Process finished with exit code 0
B线程输出,程序正常结束
验证了volatile能够保证线程可见性,在A线程操作number后,B线程能够知道A线程修改后的值,结束循环,并正常输出
结论:
volatile可以保证可见性
原子性验证
原子性:不可分割,完整性,即某个线程正在做某个业务是,中间不可以呗分割,需要整体完成,要不同时成功,要不同时失败
class MyData2{
volatile int number;
public void add() {
number++;
}
}
/**
* 验证volatile的原子性
*
* 原子性:不可分割,完整性,即某个线程正在做某个业务是,中间不可以呗分割,需要整体完成,要不同时成功,要不同时失败
*/
public class AtomicityTest {
public static void main(String[] args) {
MyData2 myData = new MyData2();
// 20个线程,每个线程调用1000次,正常清苦,最终number值应该为20000
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
myData.add();
}
}).start();
}
// 等待线程执行结束,获取myData.number,查看结果
// 后台线程:Main GC,抛开他们两个
while(Thread.activeCount() > 2) {}
System.out.println(Thread.currentThread().getName() + " get number:" + myData.number);
}
}
输出:
main get number:18576
Process finished with exit code 0
20个线程,每个线程执行1000次,理论情况输出数值应该为20000,实际与理论不符,多次执行,发现输出小于20000
解析:
number++是非线程安全的
number++会被分为三步
1、获取number当前值,记作n1——也就是上面所讲的JMM中将主内存加载到工作内存
2、将n1值加一,记作n2——在工作内存中操作
3、将n2赋值给number——将工作内存中内容重新写入主内存
Compiled from "AtomicityTest.java"
class volatileTest.MyData2 {
volatile int number;
volatileTest.MyData2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void add();
Code:
0: aload_0
1: dup
2: getfield #2 // Field number:I
5: iconst_1
6: iadd
7: putfield #2 // Field number:I
10: return
}
注意看add方法中
getfield #2 获取值
iconst_1 定义整形1
iadd 相加
putfield #2 放回去
在多线程情况下,T1获取number值为0,T2获取number值为0,在T1将number加为1时,T2获取到CPU,T1被挂起,这是主内存中number还是0,T2也将number加为1,并写会主内存,T1继续执行,再写一遍,则正常情况number现在的值应该为2,但是还是1,出现了线程操作丢失的问题,所以输出永远小于20000
那么如何解决原子性问题呢?
1、加锁Synchronized Lock
2、原子类操作 将number设为AtomicInteger
- 有序性undefined计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排,一般分为以下三种:
image.png
单线程环境里面确保程序最终执行结果和代码顺序执行的结果一直
处理器在进行重排序时必须要考虑指令之间的数据依赖性
多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测
例:
{
int x = 2; // 语句1
int y = 3; // 语句2
x = x + 5; // 语句3
y = x * x; // 语句4
}
因为Java会先对源码进行编译,那么编译后的代码顺序有可能会与源码不一致
例如语句1和语句2没有先后顺序要求,那么最终的顺序有可能是:
1234、2134、1324
但是4一定不会在前面,因为他要依赖x的值,且过程中x的值有所改变
但是多线程下指令重排可能会导致数据执行不一致的情况,无法保证数据的最终一致性
image.png
public class ReSortSeqTest {
int a = 0;
boolean flag = false;
public void method1() {
a = 1; // 语句1
flag = true; // 语句2
}
public void method2() {
if (flag) {
a = a + 5;
System.out.println("****** return:" + a);
}
}
}
如果顺序执行,那么方法2输出应该为6
但是如果在多线程且指令重排的情况下,有可能语句2先于语句1执行,但是执行完语句2,被其他线程抢占了CPU,执行方法2,那么此时a的值仍未默认值0,那么输出5,此时语句1才执行,a又被赋值为1,此时的情况我们无法准确地考虑最终的结果,但是指令重排不好演示,所以只能枯燥的讲一下,后续如果有更好的演示方式,我再进行更新
相关文章
- 职场 | 学会这几招,年底晋升不慌了!
- Github高赞:给文字打马赛克=形同虚设,AI看透你一切小心思,已开源
- Google内讧升级!员工签署联名信:AI大神Jeff Dean必须道歉!
- 20个有争议的编程观点
- IBM研究院公布计算内存和模拟AI架构重大突破
- 一个高薪的码农,应具备的8种能力
- 2020中国软件评测中心技术年会在京召开
- 谷歌赋:开启收割韭菜的新时代
- IT行业的程序员是否都在吃青春饭
- 谷歌多项服务瘫痪 媒体:太依赖GMS并不好 是该考虑华为HMS了
- 2020年度十大高薪岗位出炉:程序员霸榜
- AI把特朗普变成了dancer,跟着蔡徐坤跳起了『鸡你太美』
- Mirantis 将继续支持在 K8s 中使用 Docker
- Linux之父发话了! “警告”内核开发者不要在圣诞节前夕提交代码
- MIT发明一款计算机系统,可自动设计机器人结构,获得25年来最高成就
- 大厂纷纷“逃离”硅谷逃往德州?Oracle宣布搬离总部和马斯克做“邻居”
- 程序员如何为自己开辟出更多的收入渠道
- SAP TechEd全球技术大会在线盛大开启
- 国外几款浏览器,哪款可以在互联网上保护你的隐私
- 互联网巨头的野蛮时代结束了?更强反垄断监管即将落地