经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Java » 查看文章
【技术积累】Java里的volatile关键字到底能干嘛?自律即自由-
来源:cnblogs  作者:自律即自由-  时间:2023/8/18 9:28:50  对本文有异议

7.4 最害怕的一集 - volatile

7.4.1 最简单的一集 - volatile 语义 (难度 : ?)

读 -> 读一个 volatile 必须从 主内存读

写 -> 写一个 volatile 会把 本地内存 写到 主内存去

 

7.4.2 最好理解的一集 - volatile 保证了 可见性 ( 难度 : ? )

  1. public class VolatileSTest {
    ?
       public static void main(String[] args) throws InterruptedException {
           Data data = new Data();
           new Thread(() -> {
               while (true) {
                   if (data.bool) {
                       System.out.println("溜了溜了,线程结束了喵!");
                       break;
                  }
              }
          }).start();
    ?
           TimeUnit.SECONDS.sleep(1);
           // 保证新线程后修改
           new Thread(() -> {
               data.bool = true;
          }).start();
      }
    }
    class Data {
      // boolean bool = false;
       // volatile boolean bool = false;
    }
    ?

理解方法:

  • 如果去掉 volatile : 就会发现 死循环了

  • 如果不去掉 volatile : 就会发现 没死循环

 

自然就很好理解他的 可见性保证

7.4.3 最难复现的一集 - volatile 的 有序性保证 ( 难度 : ???? )

为什么给四颗?, 因为首先,凭空想想不到,而且真要复现出重排序很难, 个人运行了几千次线程都出不来

  1. class Number {
       int i = 1;
       volatile int v = 1;
    ?
       public void set() {
           i = 2;
           v = 2;
      }
    ?
       public void show() {
           if (v == 2) {
               System.out.println("t = " + i);
          }
      }
    }

重排序

问题一

两个线程 :

一个调用 show , 一个线程调用 set

重复一遍 !!!! 两个线程: 一个调用 show , 一个线程调用 set

重复一遍 !!!! 两个线程: 一个调用 show , 一个线程调用 set

重复一遍 !!!! 两个线程: 一个调用 show , 一个线程调用 set

因为在单线程中 , 先调用set方法 ,再调用show方法, 因为 happens-before , 是不会允许重排序的

  1. 线程一: {
          i = 2;
          v = 2;
    }
    线程二: {
          if (v == 2) {
              System.out.println("t = " + i);
          }
    }
    在不使用 volatile 的情况下
    明显,在单线程中 ,两个线程执行的方法体是没有相互依赖的
    所以是可以重排序的,所以可以出现
    线程一: {
    v = 2
    i = 2
    }
    线程二: {
    // 先读取 i (即 先准备一下sout)
    if(v == 2){
      把准备好的i输出
    }
    }
    ?
    明显会出现
    错误情况一: v = 2 , i = 1 导致 线程二输出 i = 1
    错误情况二: v = 2 , i = 2 ,但是!!因为线程二可以准备好 i ,所以线程二输出 i = 1
    都是错的
    而使用 volatile 就可以避免这些情况

Q: 欸欸欸!凭什么show一定在set之后啊?就不能先show再set吗?

A: ??干嘛?先show的话, 线程二没有输出呗,然后呢???有什么问题吗???

问题二

  1. public class Singleton {
       private volatile static Singleton instance = null;
       public  static Singleton getInstance() {
           if(null == instance) {
               synchronized (Singleton.class) {
                   if(null == instance) {
                       instance = new Singleton();
                  }
              }
          }

           return instance;

      }
    }

这个就不说了,

instancec 实例化过程:

  1. 分配内存空间
    初始化对象
    将对象指向刚分配的内存空间

变成

  1. 分配内存空间
    将对象指向刚分配的内存空间
    初始化对象

然后初始化之前又被别的线程获取到了 instance 异常, 就会出现null

 

7.4.4 最讨厌的一集 - 啥限制重排序啊 ( 难度: ????? )

还是先记一下结论再去理解叭 : 读后写前

读后写前 : volatile读后禁止一且读写重排序, volatile写前禁止一切重排序

理解:

首先要记住,都禁止重排序了, 怎么可能跟可见性有关呢 ?

一定是和 有序性相关。

  1. class Number {
       int i = 1;
       volatile int v = 1;
    ?
       public void set() {
           i = 2;
           v = 2; // 写 voaltile值
      }
    ?
       public void show() {
           if (v == 2) { // 读 volatile值
               System.out.println("t = " + i);
          }
      }
    }

首先,记住7.4.1

读 -> 读一个 volatile 必须从 主内存读

写 -> 写一个 volatile 会把 本地内存 写到 主内存去

然后:

先看看特例分析

看show方法: 如果重排序, 是不是会出现 7.4.3 中的 情况二的问题

下面是定性分析

\首先,volatile读写之间肯定要禁止重排序叭,重点不好理解的肯定是为什么 v读之后不可以普通读

对比一下:( 如果真的出现所谓 v读和后面的操作出现重排序会发生什么 )

① v读 -> 读

② v读 -> 写

③ 读 -> v读

④ 写 -> v读

① ③ 对比:

① : 读主内存,然后放到工作内存 -> 读工作内存

③ : 读工作内存 -> 读主内存,放到工作内存

有没有发现,①③普通读的工作内存来源不同?一个是之前的,一个是之后的

② ④ 对比:

② :读主内存,然后放到工作内存 -> 改变工作内存,然后刷盘到主内存

④ :改变工作内存,然后刷盘到主内存 -> 读主内存,然后放到工作内存

有没有发现,②④volatile读到的主内存来源不同?一个是之前的,一个是之后的

 

所以 读后不可以重排序

写前同理

 

7.4.5 最抽象的一集 - 内存屏障 ( 难度: ?????????? )

写的前后有 StoreStore屏障, StoreLoad屏障

读的后面又 LoadStore屏障, LoadLoad屏障

真恶心,啥跟啥啊 ?

记忆规律: 屏障两个单词, 写是不是Store, 所以是 Store + (Store / Load屏障)

读是不是Load, 所以是 Load + (Store / Load屏障)

image-20230817010351391

??????????算了吧, 要这么细是我不配了

配合 7.4.5 去理解噢,内存屏障我就是说得出来,但是就感觉稀里糊涂不知道什么玩意,就是感觉抽象,我是找不到文章能把这玩意讲的清清楚楚,看完就忘了属于是??????

 

7.4.6 最好笑的一级 - volatile干嘛的

无论是看关于volatile的视频还是文章,每次看完我都有种 “ 噢 ,牛逼 ,但是原神是由...(恼??)” 的感觉,不是吗

其实吧,如果没有涉及到 多线程公共资源的修改,

volatile 就是FW

所以大多数情况,volatile 就是个FW(大概)

但是,涉及到多线程公共资源的修改,就不一样了!!! 见7.4.6

所以它干嘛用的?

两点: ① 保证可见性 ② 保证有序性

啊 ? 那前面什么最讨厌的一集?最难复现的一集干嘛的?

给你看看什么情况下volatile居然不是废物诶!原来读前写后是这样的哇!

 

7.4.7 最震惊的一集 - 真复现出来了 ( 重排序复现 )

  1. public class OutOfOrderExecution {
       private static int i = 0, j = 0;
       private static int a = 0, b = 0;
    ?
       public static void main(String[] args) throws InterruptedException {
           int count = 0; // 计数
           while (true) {
               count++;
               i = 0;
               j = 0;
               a = 0;
               b = 0;
               Thread one = new Thread(new Runnable() {
                   @Override
                   public void run() {
                       a = 1;
                       i = b;
                  }
              });
               Thread two = new Thread(new Runnable() {
                   @Override
                   public void run() {
                       b = 1;
                       j = a;
                  }
              });
               two.start();
               one.start();
               one.join();
               two.join();
               String result = "第" + count + "次(   i= " + i + ", j= " + j + ")";
               if (i == 0 && j == 0) {
                   System.out.println(result);
                   break;
              } else {
                   System.out.println(result);
              }
          }
      }
    }
    ?

最后会出现一次 i = 0 j = 0的情况 ?!

原文链接:https://www.cnblogs.com/deyo/p/17638577.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号