问题JAVA并发情况下总是会遇到各种意向不到的问题,比如下面的代码:
int num = 0;boolean ready = false;// 线程1 执行此方法public void actor1(I_Result r) { if(ready) {r.r1 = num + num; } else {r.r1 = 1; }}// 线程2 执行此方法public void actor2(I_Result r) {num = 2; ready = true; }
- 线程1中如果发现ready=true,那么r1的值等于num + num,否则等于1,然后将结果保存到I_Result对象中
- 线程2中先修改num=2,然后设置ready=true
- r1值等于4,这个大家都能想到, CPU先执行了线程2,然后执行线程1
- r1值等于1,这个也容易理解,CPU先执行了线程1,然后执行线程2
- 那我如果说r1值有可能等于0,大家可能觉得离谱,不信的话,我们验证下 。
jcstress:全名The Java Concurrency Stress tests,是一个实验工具和一套测试工具,用于帮助研究JVM、类库和硬件中并发支持的正确性 。详细使用可以参考文章:
https://www.cnblogs.com/wwjj4811/p/14310930.html
- 生成压测工程
mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=org.openjdk.jcstress -DarchetypeArtifactId=jcstress-java-test-archetype -DarchetypeVersion=0.5 -DgroupId=com.alvin -DartifactId=juc-order -Dversion=1.0
文章插图
生成的工程代码如下图:
文章插图
- 填充测试内容
文章插图
- 方法actor1是压测第一个线程干的活,将结果保存到I_Result中 。
- 方法actor2是压测第二个线程干的活
- 类前面的@Outcome注解用来展示验证结果,特别是id="0"这个是我们感兴趣的结果
- 运行压测工程
mvn clean install java -jar target/jcstress.jar
- 查看运行结果
文章插图
- 有4000多次出现了0的结果
- 大部分情况的结果还是1和4
原因分析如果先要出现r1的值等于0,那么有一个可能0+0=0,那么也就是num=0 。
你可能想num怎么可能等于0,代码逻辑明明是先设置num=2,然后才修改ready=true, 最后才会走到num+num 的逻辑啊....
在并发的世界里,我们千万不要被固有的思维限制了,那是不是有可能num=2和ready=true的执行顺序发生了变化呢 。如果你想到这里,也基本接近真相了 。
原因: JAVA中在指令不存在依赖的情况下,会进行顺序的调整,这种现象叫做指令重排序,是 JIT 编译器在运行时的一些优化 。这也是为什么出现0的根本原因 。
指令重排不会影响单线程执行的结果,但是在多线程的情况下,会有个可能出现问题 。理解指令重排序前面提到出现问题的原因是因为指令重排序,你可能还是不大理解指令重排序究竟是什么,以及它的作用,那我这边用一个鱼罐头的故事带大家理解下 。
我们可以把工人当做CPU,鱼当做指令,工人加工一条鱼需要 50 分钟,如果一条鱼、一条鱼顺序加工,这样是不是比较慢?
文章插图
没办法得优化下,不然要喝西北风了,发现每个鱼罐头的加工流程有 5 个步骤:
- 去鳞清洗 10分钟
- 蒸煮沥水 10分钟
- 加注汤料 10分钟
- 杀菌出锅 10分钟
- 真空封罐 10分钟
文章插图
我们发现中间用很多步骤是并行做的,大大的提高了效率 。但是在并行加工鱼的过程中,就会出现顺序的调整,比如先做第二条的鱼的某个步骤,然后在做第一条鱼的步骤 。
推荐阅读
- 人生感悟的句子,睿智深刻,让人忍不住落泪!
- 张艺兴被问“小奶狗和小狼狗”意思,听完他的理解网友直呼:直男
- 高考:西周以血缘关系为宗法制,周朝分封制基础,对后世深刻影响
- 什么叫割礼女孩 什么叫割礼
- 高山流水成语故事寓意深刻 高山流水成语
- 如何理解平移函数的图像? 平移图形
- 如何理解经营策略 经营策略
- 如何理解古诗词中的意象? 什么是意象
- 怎样理解“君子之交淡如水”这句成语的? 君子之交淡如水是什么意思
- 伊能静|伊能静回应婚变:去美国是照顾儿子,感谢秦昊的理解!