zoukankan      html  css  js  c++  java
  • synchronized可见性探讨

    synchronized是jdk中的关键字,保证了原子性、可见性、有序性。本文主要探讨可见性的相关问题。可见性是指一个线程对共享变量的修改,是否对其他线程可见。JMM中规定了,lock操作会从主存中刷新最新共享变量的值到工作线程,而unlock会将工作线程中的值同步会主存。所以synchronized可以保证可见性。

    在上一篇volatile修饰数组的实验二中,出现加了 System.out.println 方法,就能顺利刷新变量的值,我们进入这个方法,可以看到

    其内部是加锁了的,锁住的是System中的常量,即 public final static PrintStream out; 。

    为了方便讨论,我们再用一个例子说明一下问题:

    public class VisibilityTest {
    
        static boolean flag = true;
    
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                while (flag) {
                    synchronized ("") {
                    }
                }
                System.out.println("退出循环");
            }).start();
            Thread.sleep(200);
    
            new Thread(() -> flag = false).start();
        }
    
    }

    第一个线程不停访问flag的值,因为使用了synchronized,所以每次都能拿到最新的flag值,第二个线程没有使用加锁,不保证第一时间将flag的修改同步回主存,但不影响我们的测试,可以看到测试结果:

    可以正常退出。

    参考资料中提到,将""替换成new Objetc(),可见性就失效了。我们实验一下,修改线程1的代码如下:

    new Thread(() -> {
        while (flag) {
            synchronized (new Object()) {
            }
        }
        System.out.println("退出循环");
    }).start();

    测试结果是无法退出循环。我怀疑是synchronized的优化,因为被用作锁对象的new Object(),是在第一个线程内部申明,所以JVM判定其他线程无法访问到这个锁对象,也就不存在竞争问题,这里的锁被消除了。为了证实我的想法,我们将锁对象提取出来

     1 public class VisibilityTest {
     2 
     3     static boolean flag = true;
     4     static Object lock = new Object();
     5 
     6     public static void main(String[] args) throws InterruptedException {
     7         new Thread(() -> {
     8             while (flag) {
     9                 synchronized (lock) {
    10                 }
    11             }
    12             System.out.println("退出循环");
    13         }).start();
    14         Thread.sleep(200);
    15 
    16         new Thread(() -> {
    17             flag = false;
    18         }).start();
    19     }
    20 
    21 }

    看行4,将锁对象定义为类变量,再次运行:

    正常退出循环。

    为什么字符串当锁就可以呢?因为不管是直接创建如 String a = "123" 还是创建字符串对象 String a = new String("123") ,都会在常量池创建常量,而常量池是可以共享的,所以JVM不会消除锁。

    参考:

    关于synchronized可见性的问题?

    人生就像蒲公英,看似自由,其实身不由己。
  • 相关阅读:
    pytorch常用函数
    检测(2):开始训练
    gcc的替换,很有用
    detection-pytorch环境配置的遇到的坑
    一些有用的ubuntu命令总结---长期更新
    如何用gdb工具进行调试
    检测(1)从0到1
    检测
    pytorch遇到的问题---不定期更新
    假名快速记忆
  • 原文地址:https://www.cnblogs.com/walker993/p/14867082.html
Copyright © 2011-2022 走看看