写点什么

【Java 深入学习】可见性

作者:Geek_65222d
  • 2022 年 10 月 08 日
    河南
  • 本文字数:1684 字

    阅读完需:约 6 分钟


基本介绍

可见性是值,一个线程对共享变量修改另一个线程可以看到最新的结果

代码示例

@Slf4j(topic = "c.Test32")public class Test32 {    static Boolean stop = false;
public static void main(String[] args) { new Thread(()->{ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } stop = true; log.debug("把stop修改为true"); }).start();
foo(); } static void foo(){ int i=0; while (!stop){ i++; } log.debug("stopped...c:{}",i); }}
复制代码


结果:


程序没有结束:


18:44:55.991 c.Test32 [Thread-0] - 把 stop 修改为 true


解释:


上述代码表达的意思是,我们创建了一个 stop 成员变量默认为 false 我们创建一个方法 foo 它里面有一个 while 循环 只有 stop 为 true 时 它才能跳出循环,然后 我们创建了一个线程 我们在它里面使 stop 变为 true,我们使线程在 0.1s 后再执行 stop=true,所以理论上来说 我们在 0.1s 后 while 循环就应该退出并打印循环次数了,可是我们却发现 这个循环并没有退出 也就是,对于 while 循环来说此时 stop 还是 false

对于上述例子的解释

解释:还是上面那张图,不过需要加一个条件 那就是每个副本在进行写操作后都会更新主存中的数据 更新后其它线程的副本也需要更新为主存中的数据 达到数据同步,但是其中会有一个问题就是 上述代码的问题 如果一个线程中有一个一直执行的代码块 就叫做热点代码块 jvm 的 jit 就会对其进行优化 以后这个代码块中的不进行写操作的变量 就会直接从副本中拿 就算有其他线程更新了主存中的数据 这个线程对于这个变量依旧从副本中拿。对于例子来说:由于 t0 多次读取 while(!stop)导致 jvm 认为 while 这部分代码块是热点代码 然后 jvm 里 jit 编译器就做了优化 因为 stop 一直只有读操作 所以以后再读取直接读取高速缓存里的 stop,但是其他代码的 stop 还用的还是最新主存拷贝的副本,所以才会出现,有一个线程更新了 stop=true 但是主线程就是获取不到 但是其他线程都可以获取到。需要注意的一点是:只有处于热点代码块中的读操作的变量 才会一直从工作内存中获取,但是有写操作的变量 每次会更新到工作内存后还会更新到主存中,大家可以试试创建一个静态变量 cnt=0 在 while 循环中 cnt++,然后其他线程在睡眠几秒后持续读取 cnt 会发现 cnt 一直在增加,也就说明 cnt 确实更新到主存中了

synchronized 的可见性

@Slf4j(topic = "c.Test32")public class Test32 {    static Boolean stop = false;    static String lock = "";    public static void main(String[] args) {        new Thread(()->{            try {                Thread.sleep(100);            } catch (InterruptedException e) {                e.printStackTrace();            }            stop = true;            log.debug("把stop修改为true");        }).start();
new Thread(()->{ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("{}",stop); }).start();
foo(); } static void foo(){ int i=0; while (true){ synchronized (lock){ if (stop) break; } i++; } log.debug("stopped...c:{}",i); }}
复制代码


结果:


19:47:45.452 c.Test32 [main] - stopped…c:5667729


19:47:45.452 c.Test32 [Thread-0] - 把 stop 修改为 true


19:47:45.546 c.Test32 [Thread-1] - true


解释


可以看出我们利用 synchronized 也可以实现 stop=true。


原因是:在 Java 内存模型中,synchronized 规定,线程在加锁时, 先清空工作内存→在主存中拷贝最新变量的副本到工作内存 →执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁。

发布于: 刚刚阅读数: 4
用户头像

Geek_65222d

关注

还未添加个人签名 2022.09.09 加入

还未添加个人简介

评论

发布
暂无评论
【Java深入学习】可见性_十月月更_Geek_65222d_InfoQ写作社区