当前位置:   article > 正文

volatile 关键字理解一(保证可见性)_volatile保证可见性

volatile保证可见性

一、概述

        volatile是Java中的关键字,用来修饰会被不同线程访问和修改的变量。

        volatile是Java虚拟机提供的轻量级的同步机制,它有三个特性:

        (1)保证可见性

        (2)不保证原子性

        (3)禁止指令重排

二、特性详解

        volatile保证可见性

        Java内存模型(JMM)定义了一组规则、规范,规定了程序中各个变量的访问方法。JMM关于同步的规定:

        (1)线程解锁前,必须把共享变量的值刷新回主内存;

        (2)线程加锁前,必须读取主内存的最新值同步到自己的工作内存;

        (3)加锁解锁必须是同一把锁;

        说明:由于JVM运行程序的实体是线程,创建每个线程时,JMM会为其创建一个工作内存(也称栈空间),工作内存是每个线程的私有数据区域。

        Java内存模型规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问。

        但是线程对变量的操作(读取、赋值等)必须在工作内存中进行。因此首先要将变量从主内存拷贝到自己的工作内存,然后对变量进行操作,操作完成后再将变量写会主内存中。

        举例说明:

        (1)火车票卖票系统还剩下一张票,并已经刷入到主内存中:ticketNum = 1;

        (2)此时有3个用户在同时购买票,3个线程都读入了目前的票数,ticketNum=1,那么线程就会继续进入购买流程。

        (3)假设其中一个线程先抢占了CPU资源,先买到票,并将自己的工作内存中的ticketNum值改为0,ticketNum=0,然后再写回到主内存。

        这时,由于一个线程的用户已经买到了票,那么其他用户的线程应该不能再继续进入购买票的流程了,因此需要系统通知到其他线程 ticketNum=0 这个消息。如果可以达到这样的效果,可以理解为 具有可见性

        无可见性代码演示:

  1. @Test
  2. public void test1() {
  3. DataDemo dataDemo = new DataDemo();
  4. RunThread runThread = new RunThread(dataDemo);
  5. runThread.start();
  6. while (dataDemo.getNumber() == 0) {
  7. }
  8. System.out.println("具有可见性验证通过");
  9. }
  10. public class DataDemo {
  11. private int number = 0;
  12. public void add() {
  13. this.number = this.number + 10;
  14. }
  15. public int getNumber() {
  16. return number;
  17. }
  18. }
  19. public class RunThread extends Thread {
  20. private DataDemo dataDemo;
  21. public RunThread(DataDemo dataDemo) {
  22. this.dataDemo = dataDemo;
  23. }
  24. @Override
  25. public void run() {
  26. System.out.println("线程[" + Thread.currentThread().getName() + "] 正在执行");
  27. try {
  28. TimeUnit.SECONDS.sleep(3);
  29. } catch (InterruptedException e) {
  30. e.printStackTrace();
  31. }
  32. dataDemo.add();
  33. System.out.println("线程[" + Thread.currentThread().getName() + "]更新后,number值为:" + dataDemo.getNumber());
  34. }
  35. }
  36. 执行结果:
  37. 线程[Thread-0] 正在执行
  38. 线程[Thread-0]更新后,number值为:10

                结果分析:

        可以看出子线程启动后将number值改为了10,虽然已经改为了非0,但是主线程仍然一直处于while循环中,因此此时number不具有可见性,系统不会主动通知主线程number值修改。

        原理说明:

        这个问题其实就是私有堆栈中的值和公共堆栈中的值不同步造成的。解决这样的问题就要使用 volatile 关键字了,它主要的作用就是当线程访问number这个变量时,强制性从公共堆栈中进行取值。

                                        

        

        可见性代码演示:

  1. @Test
  2. public void test1() {
  3. DataDemo dataDemo = new DataDemo();
  4. RunThread runThread = new RunThread(dataDemo);
  5. runThread.start();
  6. while (dataDemo.getNumber() == 0) {
  7. }
  8. System.out.println("具有可见性验证通过");
  9. }
  10. public class DataDemo {
  11. // 给变量 number 添加 volatile 关键字修饰
  12. volatile private int number = 0;
  13. public void add() {
  14. this.number = this.number + 10;
  15. }
  16. public int getNumber() {
  17. return number;
  18. }
  19. }
  20. public class RunThread extends Thread {
  21. private DataDemo dataDemo;
  22. public RunThread(DataDemo dataDemo) {
  23. this.dataDemo = dataDemo;
  24. }
  25. @Override
  26. public void run() {
  27. System.out.println("线程[" + Thread.currentThread().getName() + "] 正在执行");
  28. try {
  29. TimeUnit.SECONDS.sleep(3);
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. dataDemo.add();
  34. System.out.println("线程[" + Thread.currentThread().getName() + "]更新后,number值为:" + dataDemo.getNumber());
  35. }
  36. }
  37. 执行结果:
  38. 线程[Thread-0] 正在执行
  39. 线程[Thread-0]更新后,number值为:10
  40. 具有可见性验证通过

        结果分析:

        通过对变量number变量添加了volatile关键字修饰,可以看出子线程启动后将number值改为了10,随后主线程跳出了while循环,输出了“具有可见性验证通过”,说明此时number具有可见性。

        原理说明:

        通过使用 volatile 关键字,强制从公共内存中读取变量的值,内存结构如图:

                

        由于本人水平有限,本博客可能存在不足和错误在所难免,还请大家多多指正。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/44970
推荐阅读
  

闽ICP备14008679号