之前的时候看《并发编程的艺术》,书中提到dcl写法的单例模式是有问题的,有可能会导致调用者得到一个创建了一半的对象,从而导致报错。修复办法是将单例对象的引用添加volatile进行修饰,禁用重排序,则外界获取的就一定是已经创建好的对象了。
  光说总是不行的,上代码:

public class SingleTest {
private static SingleTest singleTest; // 这个应该用volatile修饰
//获取单例的方法
public static SingleTest getInstance() {
if(singleTest == null){
synchronized (SingleTest.class){
if(singleTest == null){
singleTest = new SingleTest();
}
}
}
return singleTest;
}
}

  对于这一段的分析说的很清楚,网上也有大量的文章,但我有一个疑问:不是说synchronized有原子性、可见性么,而且可见性是通过monitor exit的时候强制刷新内容到主内存来实现的,既然这样,那synchornized结束前,没有刷新到内存,外面的程序应该读不到这个单例对象的值才对啊,为什么会读到呢?这个synchronized 的可见性究竟该怎么理解?
  先说理解的错误之处:synchronized的可见性是通过monitor exit来保证的,这点没错,但monitor exit之前就不会刷新到主内存么,显然不是。现在jvm的机制,已经尽量快速的将改变同步到缓存了,这个机制是怎么确定的不清楚,但简单测试会发现非常短。
  另外,synchronized 的可见性的正确理解是:对于被synchronized修饰的代码块,如果A线程执行结束,会强制刷新线程缓存内容到内存,同时通知其它synchronized修饰的线程x的值无效,需要重新读取(这点跟volatile很相似),因此B线程在执行的时候也就能读到A线程对x的修改了,这就是synchronized的可见性。

  试一下如下示例:

//可见性验证
@Test
public void testA() throws InterruptedException {
//启动线程在不停监视str变化
Thread th1 = new Thread(() -> {
while(true){
if(str.equals("b")){
System.out.println("th1 ==> str 已经被改为 b ," + Thread.currentThread());
}
}
});
Thread th2 = new Thread(() -> {
while(true){
synchronized (str){
if(str.equals("b")){
System.out.println("th2 ==> str 已经被改为 b ," + Thread.currentThread());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
th1.start();
th2.start(); //让监视线程都启动完毕
Thread.sleep(3000); System.out.println("修改str的值为b");
synchronized (str){
str = "b";
} Thread.sleep(3000);
}
  执行结果:

  可以看到th1并没有输出,因为它线程中的str换出内容一致是“a”。实际上,33-35行可以不用synchronized,也会有相同结果,因为现在的jvm会尽最快速度将改变同步到缓存,而synchronized在执行的时候会重新读取,因此也会发现str的值被改变了,而th1则没有重新读取的机制,也就无法进行输出了。
  对于monitor exit之前也会刷新到内存这点,也可以通过程序进行验证,可以在synchronized中修改某个值,然后sleep一段时间,这期间让另一个线程去读取被改变的值,会发现其实是可以读到的。

synchronized的可见性理解的更多相关文章

  1. synchronized内存可见性理解

    一.背景 最近在看<Java并发编程实战>这本书,看到共享变量的可见性,其中说到"加锁的含义不仅仅局限于互斥行为,还包括内存可见性". 我对于内存可见性第一反应是vol ...

  2. 原子性、可见性、synchronized 有好理解

    原子性.可见性.synchronized 有好理解: from: https://blog.csdn.net/wohaqiyi/article/details/67635010 1.原子性 (1)原子 ...

  3. java synchronized实现可见性对比volatile

    问题: 大家可以先看看这个问题,看看这个是否有问题呢? 那里有问题呢? public class ThreadSafeCache { int result; public int getResult( ...

  4. java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解

    synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...

  5. java学习:JMM(java memory model)、volatile、synchronized、AtomicXXX理解

    一.JMM(java memory model)内存模型 从网上淘来二张图: 上面这张图说的是,在多核CPU的系统中,每个核CPU自带高速缓存,然后计算机主板上也有一块内存-称为主内(即:内存条).工 ...

  6. 关于synchronized 影响可见性的问题

    问题来自于学习thinking in java的时候的一个示例,先上代码吧 public class StopThread { private static boolean stop = false; ...

  7. Volatile和Synchronized对可见性和原子性的支持

    在学习并发编程的时候,遇见了volatile和synchronized关键字问题,volatile是可以保证可见性,但无法保证原子性,synchronized关键字由于其是加锁机制,肯定是可以保证原子 ...

  8. synchronized实现可见性

    JMM关于synchronized的两条规定: 1)线程解锁前,必须把共享变量的最新值刷新到主内存中 2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值 ( ...

  9. java线程-synchronized实现可见性代码

    以下是一个普通线程代码: package com.Sychronized; public class SychronizedDemo { //共享变量 private boolean ready=fa ...

随机推荐

  1. Integer.MIN_VALUE

    static int MAX_VALUE           值为 2^31-1 的常量,它表示 int 类型能够表示的最大值. static int MIN_VALUE           值为 - ...

  2. 高性能无锁队列 Disruptor 初体验

    原文地址: haifeiWu和他朋友们的博客 博客地址:www.hchstudio.cn 欢迎转载,转载请注明作者及出处,谢谢! 最近一直在研究队列的一些问题,今天楼主要分享一个高性能的队列 Disr ...

  3. 徒手CPR心脏复苏

    CPR 缩写于cardiopulmonary resuscitation. 在危难时刻,能救人救命,意义极其重大,赶紧学起来 成人的CPR 第一步:检查意识 靠近其耳朵,在两耳旁交替大声喊:「你怎么了 ...

  4. iOS开发其他相关

    1.iOS开发行情 1.1 iOS系统各个版本的占比查询 2.Xcode的使用 开发软件下载 Xcode Help(官方) 2.1 Xcode面板 Xcode面板 2.2 Xcode版本新功能 Xco ...

  5. “全栈2019”Java第八十三章:内部类与接口详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  6. node.js的总结-可以应付bat的社招面试

    什么是NodeJS Node.js采用模块化结构,按照CommonJS规范定义和使用模块.模块与文件是一一对应关系,即加载一个模块,实际上就是加载对应的一个模块文件. JS是脚本语言,脚本语言都需要一 ...

  7. ubuntu下面的某些软件安装

    1. python 下面的mysql驱动:不是在pip里面安装,执行下面命令 apt-get install python-mysqldb

  8. 2016级算法第一次练习赛-D.AlvinZH的儿时回忆——跳房子

    864 AlvinZH的儿时回忆----跳房子 题目链接:https://buaacoding.cn/problem/864/index 思路 这是一道简单题,但是同样有人想复杂了,DP?大模拟?. ...

  9. js和jquery获取属性的区别

    一.获取元素: js获取元素: 根据id获取:document.getElementById("id"); 根据类名获取:document.getElementsByClassNa ...

  10. 虚拟机上使用 opecnv 读取USB摄像头无法显示

    使用opecv读取USB摄像头时候,无法显示图像. 设置 首先查看虚拟机Ubuntu检测摄像头是否已正常插入: ls /dev/video* 结果为: 设置虚拟机USB属性: USB的兼容性设置为US ...