一个关于多线程的问题

Posted by CaiJiahe on October 30, 2017

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的语义,所以每个线程缓存主内存的变量并没有读到最新的,所以导致一直在死循环。