关键字volatile的含义及使用,volatile关键字在什么阶段起作用

  关键字volatile的含义及使用,volatile关键字在什么阶段起作用

  

  如何解决写爬虫IP受阻的问题?立即使用。

  深入理解volatile关键字

  1.volatile与可见性

  大家都知道volatile可以保证可见性,那么是怎么保证的呢?

  这就方便了“发生之前”的原则。这个原理的第三条规则规定:对于volatile修改的变量,写操作早于这个变量的读操作。具体步骤如下:

  线程A将共享变量读入工作内存,线程B也将共享变量读入工作内存。

  线程A修改共享变量后,会立即刷新到主存。此时,线程B的工作内存中的共享变量将被设置为无效,需要从主存中重新读取新值。反映硬件上CPU的缓存线被设置为无效状态。

  这确保了可视性。简单来说,一个线程修改volatile修改的变量并刷新到主存后,会使其他线程工作内存中的共享变量失效,需要重新从主存中读取。

  2.volatile与有序性

  大家都知道volatile可以保证秩序,那怎么保证呢?

  Volatile保证有序,相对直接。禁止JVM和处理器对volatile关键字修改的变量的指令进行重新排序,但变量之前或之后的变量可以任意排序,只要最终结果与未修改的结果一致即可。

  底层原理

  volatile修改的变量会在底部加上“lock:”前缀,带‘lock’前缀的指令相当于一个内存屏障,是保证可见性和顺序的关键。该屏障的功能如下:

  指令重排时,不能将屏障前的代码重排到屏障后,也不能将屏障后的代码重排到屏障前。

  在执行内存屏障时,要确保前面的代码都已经执行了,并且屏障后面的代码可以看到执行结果。

  强制将工作内存中的变量刷新到主内存中。

  其他线程的工作内存中的变量将被设置为无效,需要从主内存中重新读取。

  3.volatile与原子性

  大家都知道volatile不能保证原子性,为什么不能呢?

  代码演示:

  包com . github . excellent 01;

  导入Java . util . concurrent . countdownlatch;

  /**

  * @作者plg

  * @日期2019/5/19 9:37

  */

  公共类TestVolatile实现Runnable {

  私有可变整数num=0;

  私有静态CountDownLatch latch=new CountDownLatch(10);

  @覆盖

  公共无效运行(){

  for(int I=0;i 1000i ){

  num

  }

  latch . count down();

  }

  公共整数getNum() {

  退货数量;

  }

  公共静态void main(String[] args)引发InterruptedException {

  test volatile test=new test volatile();

  for(int I=0;i 10i ){

  新线程(测试)。start();

  }

  latch . await();

  system . out . println(test . getnum());

  }

  }启动10个线程,每个线程给共享变量num加1000次。当所有线程完成后,打印出num的最终结果。

  很少有一万个,这是因为volatile不能保证原子性。

  原因分析:

  num++的操作由三步组成:

  将num从主存储器读入工作存储器

  在工作记忆中增加一个。

  一旦加法完成,就把它写回主存。

  这三个步骤虽然都是原子操作,但放在一起不是原子操作,每个步骤在执行的过程中都有可能被打断。

  此时假设num的值为10,线程A将变量读入自己的工作内存。此时发生CPU切换,B也将num读入自己的工作内存。此时线程B修改了自己工作内存中num的值,变成了11,但是还没有刷新到主存,所以线程A不知道num的值已经改变了。如前所述,修改volatile变量后,其他线程会立即知道,前提是先刷新到主存中,然后其他线程会将自己工作中共享变量的值设置为无效。因为没有刷新到主存,A傻乎乎的不知道给10加了一,所以最后虽然两个线程都加了一个,但是最后结果只加了一次。

  这就是为什么volatile不能保证原子性。

  volatile的使用场景

  根据volatile的特性,可以保证有序性和可见性,但不能保证原子性。因此,volatile可以用于不需要原子性,或者原子性已经得到保证的情况:

  代码演示

  可变布尔关闭请求

  公共void shutdown() {

  shutdownRequested=true

  }

  公共无效工作(){

  while(关闭请求){

  //做事情

  }

  }只要线程修改shutdownRequested,执行工作的线程就会立刻看到,所以会立刻停止。如果没有添加volatile,那么每次它去工作内存读取数据的时候总是真的。它一直执行,不知道别人已经停止了。

  代码演示:

  包com . github . excellent;

  导入Java . util . concurrent . threadpoolexecutor;

  /**

  *启动线程会被阻塞,从内存中读取标志并存入寄存器,下次直接从寄存器中取值。

  *所以值总是假的。

  *即使另一个线程更改了值,它也不知道。

  *添加挥发物。也可以锁定,只要保证内存可见性。

  * @作者plg

  * @日期2019/5/2 22:40

  */

  公共类Testvolatile {

  公共静态布尔标志=false

  公共静态void main(String[] args)引发InterruptedException {

  Thread thread1=新线程(()-{

  for(;) {

  system . out . println(flag);

  }

  });

  Thread thread2=新线程(()-{

  for(;){

  flag=true

  }

  });

  thread 1 . start();

  thread . sleep(1000);

  thread 2 . start();

  }

  }执行结果:

  太蠢了,别人修改了,自己不知道,还输出false。加个挥发物就行了。以上是深入了解volatile关键词的细节。更多请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: