一道诡异的volatile影响线程间可见性的问题
来源:13-1 volatile-

qq_凛冬将至_9
2023-01-14
老师小年好,翻看Java多线程面试复习资料时,想到了之前整理的一个问题,不清楚造成的原因是什么,真的很诡异。。。
主程序
package com.pf.java.thread.learning.c004_visibility;
import com.pf.java.thread.learning.util.SleepHelper;
public class Test_05_volatile_obj {
static class Counter {
/*volatile*/ int i; // 1
void increment() {
i++;
}
public int get() {
return i;
}
}
// 2
/*volatile*/ Counter counter = new Counter();
void increment() {
counter.increment();
}
int get() {
return counter.get();
}
public static void main(String[] args) {
Test_05_volatile_obj c = new Test_05_volatile_obj();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
c.increment();
System.err.println("count " + c.get()); // 3
SleepHelper.sleepMilliSeconds(1000); // 4
}
}, "t1").start();
new Thread(() -> {
while (true) {
if (c.get() == 5) {
break;
}
}
System.err.println("t2 结束");
}, "t2").start();
}
}
辅助工具类:
package com.pf.java.thread.learning.util;
import java.util.concurrent.TimeUnit;
/**
* Sleep帮助类,这里不考虑休眠被打断的情况下可以使用,仅测试目的,不可用于生产环境
*/
public class SleepHelper {
public static void sleepSeconds(int s) {
try {
TimeUnit.SECONDS.sleep(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void sleepMicroSeconds(int s) {
try {
TimeUnit.MICROSECONDS.sleep(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void sleepMilliSeconds(int s) {
try {
TimeUnit.MILLISECONDS.sleep(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这个程序的意图很简单:两个线程t1、t2分别对共享的计数器变量count进行操作和监听,t1线程循环10次对count
中的i
执行加1,t2线程不停的监听count
中的i
,监听到值为5则结束线程。现在发现程序的运行有一个诡异的现象:t1线程每次循环会打印计数器的值并休眠1秒,在1、2两处注释掉的情况下,t2线程监听不到,也就是计数器变量对t2是不可见的。如果1、2两处放开一处的注释,t2就能监听到!也就是volatile修饰对象引用或者对象中的字段都能确保该字段的可见性,这是我不理解的一个地方!另外一个问题,如果3、4两处都注释掉,而放开1、2两处,发现t2还是监听不了,volatile不是本身能确保可见性吗,为啥还一定要配合控制台打印(底层用到synchronized)或者sleep结束后从主存刷新这样的机制,这是我不理解的另一个地方!
老师按照我的表示运行下程序,然后帮我看看问题出在哪里,感谢啦(●’◡’●)
写回答
2回答
-
张婧仪
5天前
我来解答你的疑问。
第一个问题,如果Volatile修饰i,肯定保证可见性。如果修饰Counter,因为i属于Counter ,所以i变化,Counter 也会变,也会保证可见性。
第二个问题,受处理器调度影响,t2线程并不是一直监听,所以让t1加入睡眠走的慢点,可以监听到。如果不加睡眠,t1走的太快,t2就可能监听不到。
00 -
悟空
2023-01-26
00
相似问题