Java多线程关键字volatile的理解

最近在学习Java多线程相关的内容,学到了volatile关键字,在以前的面试中也遇到了这个问题。

首先,先来看个例子:

public class MyThread extends Thread {
    volatile public static int count;
    private static void addCount() {
        for (int i=0; i<100; i++) {
            count++;
            System.out.println("当前线程:" + Thread.currentThread().getName() + ",count=" + count);
        }
        System.out.println("count=" + count);
    }

    @Override
    public void run() {
        addCount();
    }

    public static void main(String[] args) {
        MyThread[] myThreads = new MyThread[100];
        for (int i=0; i<100; i++) {
            myThreads[i] = new MyThread();
        }

        for (int i=0; i<100; i++) {
            myThreads[i].start();
        }
    }
}

输出的最终结果多次运行都不一样,说明关键字volatile并未使得count值同步。

关键字volatile是多线程之间能够感知变量被更改了(可见性),使得线程使用的是最新的值。

要理解这个关键字,还需要了解多核cpu的缓存结构等知识。相关资料我就不介绍了,请参照文后的参照。

每个线程都有各自的工作内存,而volatile关键字修饰的变量存储在主工作内存(共享内存)中。线程每次使用到这个变量时,都会去主工作内存中去读区最新的值拷贝到当前线程的缓存中。

这里有以下几种情况:

第一种:主工作内存中的变量未做修改,那么每个线程从主工作内存中拷贝最新的值到当前线程缓存中使用,然后每个线程都等待主内存变量修改的通知,没有通知就使用当前缓存中的值;

第二种:众多线程中,某个线程要修改这个变量了,会怎么办呢?首先,先对这个变量上锁(lock),然后修改这个值,修改完成后,通知其他线程主工作内存变量的值已经更改了。此时,其他线程收到通知后会主动拷贝主工作内存中变量的值,覆盖当前线程共享内存中的值(也就是说,不管当前线程的值是否已经修改,都会拿主工作内存的值覆盖当前线程缓存中的值)。

拿前面代码的例子来讲:

有100个线程都在执行addCount动作,肯定有一个线程先去修改主内存变量;那么其他线程在没有收到变量修改通知之前,还是使用当前线程缓存中的值;即,假设线程A将count值(从0修改为1了)进行修改,在未修改完成之前(也就是说还未发出通知)那么其他线程还是使用的值还是0。当线程A修改好了,主线程变量已经是1了,那么会通知其他线程来读取最新的值,此时,线程会放弃当前已经运行最新的值,哪怕比1还要大,都要放弃,而采用主内存变量值1。因此,前面的那个例子,最终输出的结果不唯一的原因也就在于此。

再细说一下:

100个线程,A线程已经将主工作内存中的count值更新到了4,而线程B刚刚计算完,线程内count值为2,获得了修改主内存变量的锁,那么线程B就会去修改主工作内存中的count值为2。

很多文章都说volatile不具有原子性,我没搞懂这个关键字和原子性有啥关系。

关于关键字volatile的底层原理,请参照文章:http://www.cnblogs.com/xrq730/p/7048693.html

猜你喜欢

转载自my.oschina.net/OHC1U9jZt/blog/1807893