一文读懂艾娃中波动的关键字使用

  

在本文中,我们会介绍java中的一个关键字volatile.挥发性的中文意思是易挥发的,不稳定的。那么在java中使用是什么意思呢?

  

我们知道,在java中,每个线程都会有个自己的内存空间,我们称之为工作记忆。这个空间会缓存一些变量的信息,从而提升程序的性能。当执行完某个操作之后,线程会将更新后的变量更新到主缓存中,以供其他线程读写。

  

因为变量存在工作记忆和主存两个地方,那么就有可能出现不一致的情况。那么我们就可以使用不稳定的关键字来强制将变量直接写到主内存,从而保证了不同线程读写到的是同一个变量。

  

  

那么我们什么时候使用波动呢?当一个线程需要立刻读取到另外一个线程修改的变量值的时候,我们就可以使用波动。我们来举个例子:

        公开课VolatileWithoutUsage {   私人int数=0;      公共空间incrementCount () {   数+ +;   }   公共int getCount () {   返回计数;   }   }      之前      

这个类定义了一个incrementCount()方法,会去更新计算值,我们接下来在多线程环境中去测试这个方法:

        @Test   公共空间testWithoutVolatile()抛出InterruptedException {   ExecutorService服务=Executors.newFixedThreadPool (3);   VolatileWithoutUsage VolatileWithoutUsage=new VolatileWithoutUsage ();      IntStream.range (0, 1000)。forEach(计数→service.submit (volatileWithoutUsage:: incrementCount));   service.shutdown ();   服务。awaitTermination(1000年,TimeUnit.MILLISECONDS);   assertequal(1000年,volatileWithoutUsage.getCount ());   }      

运行一下,我们会发现结果是不等于1000的。

  
  

. lang。AssertionError:
  预期:1000
  Actual ,:999

     

这是因为多线程去更新同一个变量,我们在上篇文章也提到了,这种情况可以通过加同步关键字来解决。

  

那么是不是我们加上动荡的关键字后就可以解决这个问题了呢?

        公开课VolatileFalseUsage {   私人波动int数=0;      公共空间incrementCount () {   数+ +;   }   公共int getCount () {   返回计数;   }      }      之前      

上面的类中,我们加上了关键字不稳定,我们再测试一下:

        @Test   公共空间testWithVolatileFalseUsage()抛出InterruptedException {   ExecutorService服务=Executors.newFixedThreadPool (3);   VolatileFalseUsage VolatileFalseUsage=new VolatileFalseUsage ();      IntStream.range (0, 1000)。forEach(计数→service.submit (volatileFalseUsage:: incrementCount));   service.shutdown ();   服务。awaitTermination(5000年,TimeUnit.MILLISECONDS);   assertequal(1000年,volatileFalseUsage.getCount ());   }      

运行一下,我们会发现结果还是错误的:

  
  

. lang。AssertionError:
  预期:1000
  Actual ,:992
  ~ ~

     

为什么呢?我们先来看下数+ +的操作,计数+ +可以分解为三步操作,1。读取数的值2。给计数加1,3。将数量写回内存。添加动荡的关键词只能够保证数的变化立马可见,而不能保证1、2、3这三个步骤的总体原子性。要实现总体的原子性还是需要用到类似同步的关键字。

  

下面看下正确的用法:

        公开课VolatileTrueUsage {      私人波动int数=0;      公共空间setCount (int数){   数=数量;   }   公共int getCount () {   返回计数;   }   }      @Test   公共空间testWithVolatileTrueUsage()抛出InterruptedException {   VolatileTrueUsage VolatileTrueUsage=new VolatileTrueUsage ();   线程threadA=新线程(()→volatileTrueUsage.setCount (10));   threadA.start ();   thread . sleep (100);      线程读者=新线程(()→{   int valueReadByThread=volatileTrueUsage.getCount ();   valueReadByThread assertequal(10日);   });   reader.start ();   }   # #之前      

从java5之后,挥发性提供了一个之前的功能.Happens-Before是指当不稳定进行写回主内存的操作时,会将之前的非挥发性的操作一并写回主内存。

        公开课VolatileHappenBeforeUsage {      int=0;   不稳定的布尔标志=false;      公共空间的作家(){=1;//1线程一个修改共享变量   国旗=true;//2线程写挥发性变量   }   }   

一文读懂艾娃中波动的关键字使用