美文网首页
volatile关键字

volatile关键字

作者: 进阶的小豆子 | 来源:发表于2018-07-30 15:23 被阅读0次

       在学习什么是volatile之前,首先应该认识一下Java内存模型。Java中的内存模型如图所示:

    Java内存模型.jpg

       主内存:Java内存模型规定了所有的变量都存储在主内存中。主内存被所有的线程所共享。

       工作内存:每条线程中都有自己的工作内存。可以简单地理解为计算机中的CPU高速缓存,但又不完全相同。线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来),相当于“副本”。线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。

       不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

       那么为什么不在主内存上进行直接的操作呢?让我们一起来看一个例子:

       假设有这样一个静态变量:

    static int i = 0;
    

       线程A执行如下代码:

    i = 1;
    

       那么Java内存模型工作流程如下:

       首先主内存中i=0;线程A把静态变量i=0从主内存读到工作内存;然后再执行i=1操作,最后再把i=1 这个结果同步更新到主内存中。对于单线程来说,这个过程没有任何问题。

       此时,我们引入线程B,执行如下操作:

     System.out.println("i = " + i);
    

       如果线程A先执行,线程B后执行,线程B的输出结果会是什么呢?有两种可能,是1或者0。

       更大的可能是i=1;修改成功,原理很简单不再赘述。我们来看下另外一种i=0的可能。

       首先,主内存中i=0;线程A把静态变量i=0从主内存读到工作内存;然后再执行i=1操作,因为工作内存所更新的变量并不会立即同步到主内存,所以虽然线程A在工作内存当中已经把变量i更新为1,但是线程B从主内存中获取的仍然是0,从而得到一个没有立即同步更新的结果 i=0。

       那么这种问题如何解决呢?关键字volatile就可以很好地解决这个问题。Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主内存中,当有其他线程需要读取时,它会去主内存中读取新值。为什么volatile关键字可以有这样的特性?这主要受益于java语言的先行发生原则(happens-before),即不需要通过任何手段就能够得到保证的有序性。

       在上述例子中,如果在静态变量i之前加上volatile修饰符,就不会产生i=0的结果了:

    volatile static int i = 0;
    

       线程A、B依旧进行上个例子的操作,这样得到的结果一定就是i=1。

       但是,volatile只能保证共享变量的可见性,不能保证变量的原子性。并且,一旦一个共享变量被volatile修饰了之后,禁止指令重排序。

       小结:volatile除了保证可见性和阻止指令重排,但是不能保证原子性。

    相关文章

      网友评论

          本文标题:volatile关键字

          本文链接:https://www.haomeiwen.com/subject/edefvftx.html