阿男的小窝

View the Project on GitHub

通过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 2waiting变量的修改,并没有被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的架构对编程的影响,还可以看看之前写过的这两篇文章: