通过Java的volatile关键字来看CPU multi-core的cache line
本文通过代码实例来讲解多核CPU的架构对并发编程的影响。
撰写代码如下:
package concurrent.volatiledemo;
/**
* Created by weli on 6/20/16.
*/
public class NoVolatile {
boolean waiting = true;
public void test() {
new Thread(new Runnable() {
public void run() {
while (waiting == true) {
// wait
}
System.out.println("Thread 1 finished.");
}
}).start();
new Thread(new Runnable() {
public void run() {
// Sleep for a bit so that thread 1 has a chance to start
try {
Thread.sleep(100);
} catch (InterruptedException ignored) {
}
System.out.println("Thread 2 shutdown...");
waiting = false;
}
}).start();
}
public static void main(String[] args) {
new NoVolatile().test();
}
}
如上所示,我们使用waiting
这个变量作为两个threads之间的协同信号,来控制两个threads的工作顺序。但是执行上面的代码,结果如下:
可以看到,thread 1
并没有被执行,并且陷入在自己的循环当中。说明thread 2
对waiting
变量的修改,并没有被thread 1
看到。
这是因为,在CPU multi-core的情况下,一个core对变量做的修改,可能只是修改了自己这个core所属的缓存,并没有更新到实际的,各个core都能看见的,公有的内存线上去。
因此,对于这中需要多个threads共同读写的变量,我们要加上volatile
关键字才可以。下面是修改后的代码:
public class UseVolatile {
volatile boolean waiting = true;
public void test() {
new Thread(new Runnable() {
public void run() {
while (waiting) ;
System.out.println("Thread 1 finished.");
}
}).start();
new Thread(new Runnable() {
public void run() {
// Sleep for a bit so that thread 1 has a chance to start
try {
Thread.sleep(100);
} catch (InterruptedException ignored) {
}
System.out.println("Thread 2 shutdown...");
waiting = false;
}
}).start();
}
public static void main(String[] args) {
new UseVolatile().test();
}
}
上面的代码是使用了volatile
关键字后的代码。执行上面的代码,结果如下:
可以看到结果是和预期的一致。
因此我们要理解,在多核CPU的环境下,因为多级缓存的存在,和每个CPU的local cache的存在,所以CPU对数据的操作变得不那么直观,因此在多线程编程的时候,要特别小心。
关于并发编程和多核CPU的架构对编程的影响,还可以看看之前写过的这两篇文章:
- 上一篇 SPACEMACS使用随笔
- 下一篇 对non-lock queue的简单分析