0x01 问题
最近有时间,整理个问题。校招的小徒弟8月某天写了一段代码,想使用三个线程按照ABC的顺序无限输出。但是发现没有按照预期输出,控制台输出一会就没有输出的,所以有些小疑惑。代码如下。
public class ThreadPrint implements Runnable{
Logger logger = Logger.getLogger(ThreadPrint.class.getName());
public static Object obj = new Object();
private final String name;
private final int flag;
public ThreadPrint(String name, int flag) {
this.name = name;
this.flag = flag;
}
public void run() {
while (true) {
if ((ThreadUtil.count % 3) == flag) {
synchronized (ThreadUtil.class) {
ThreadUtil.count ++;
System.out.println(name + ThreadUtil.count);
}
}
}
}
public static void main(String[] args) {
new Thread(new ThreadPrint("A",0),"Thread-A").start();
new Thread(new ThreadPrint("B",1),"Thread-B").start();
new Thread(new ThreadPrint("C",2),"Thread-C").start();
}
}
class ThreadUtil {
static int count = 0;
}
0x02 解决办法
将count变量加上volatile修饰符程序就能按照预期输出了。但是不加volatile修饰为什么会不输出了呢?
在程序运行但是不输出的时候,观察cpu占用,发现有三个核被吃满了。那么问题就已经找到原因了,上面的三个线程都变成了死循环,但是并不满足(ThreadUtil.count % 3) == flag这个条件,所以无法输出。
因为没有volatile的修饰,所以整段代码没有happen-before的语义,所以每个线程缓存主内存的变量并没有读到最新的,所以导致一直在死循环。