一、背景

最近在看<Java并发编程实战>这本书,看到共享变量的可见性,其中说到“加锁的含义不仅仅局限于互斥行为,还包括内存可见性”。

我对于内存可见性第一反应是volatile:被volatile修饰的变量能够保证每个线程能够获取该变量的最新值,从而避免出现数据脏读的现象。

原因是volatile修饰的共享变量进行写操作的时候会多出Lock前缀的指令,通过多处理器的缓存一致性协议,来保持变量的同步更新。

但是我却没明白“加锁”与“可见性”这句话表达的意思,仔细思考下确实是这样子的。

二、实践

 public class NoSivibilityVariable {
private static boolean isOver = false;
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (!isOver);
System.out.println("thread ----- true");
}
});
thread.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
isOver = true;
System.out.println("main ----- true");
}
}

执行上面这段代码,只会打印出“main ----- true”然后一直死循环阻塞。原因是当主线程把变量isOver修改为true,值的修改作用范围仅仅是当前线程内(主线程)而另外的线程是主内存的值,并没有读取主线程修改后的值,所以另一个线程和主内存的值都是失效的值。

如果要解决这个情况怎么办?

1.常见的做法就是把第二行 private static boolean isOver = false修改为 private static volatile boolean isOver = false就不会出现这种情况。

2.就像书中所说的通过加锁来解决。

 package synchronized_word;

 public class NoSivibilityVariable {
private static boolean isOver = false; public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (NoSivibilityVariable.class) {
while (!isOver);
System.out.println("thread ----- true");
}
}
});
thread.start(); try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (NoSivibilityVariable.class) {
isOver = true;
}
System.out.println("main ----- true");
}
}

2个线程中在对共享变量的读取或者写入都进行加锁处理,因为线程对应的都是同一把锁对象(该类对象)所以相互会排斥。但是就算这样子也不能说明内存可见性的。其实真正解决这个问题的是JMM关于Synchronized的两条规定:

1、线程解锁前,必须把共享变量的最新值刷新到主内存中; 
2、线程加锁时,讲清空工作内存中共享变量的值,从而使用共享变量是需要从主内存中重新读取最新的值(加锁与解锁需要统一把锁)

线程执行互斥锁代码的过程: 
1.获得互斥锁 
2.清空工作内存 
3.从主内存拷贝最新变量副本到工作内存 
4.执行代码块 
5.将更改后的共享变量的值刷新到主内存中 
6.释放互斥锁

http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#incorrectlySync

这里提到synchronized会保证对进入同一个监视器的线程保证可见性。比如线程 t1修改了变量,退出监视器之前,会把修改变量值v1刷新的主内存当中;当线程t2进入这个监视器时,如果有某个处理器缓存了变量v1,首先缓存失效,然后必须重主内存重新加载变量值v1(这点和volatile很像)。这里语义的解读只是说了对于同一个监视器,变量的可见性有一定的方式可寻,非同一个监视器就不保证了

三、总结

synchronized具有内存可见性,为了确保所有线程能够看到共享变量的值是最新的,所有执行读操作或写操作的线程都必须在同一个锁上同步。

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

  1. synchronized的可见性理解

    之前的时候看<并发编程的艺术>,书中提到dcl写法的单例模式是有问题的,有可能会导致调用者得到一个创建了一半的对象,从而导致报错.修复办法是将单例对象的引用添加volatile进行修饰,禁 ...

  2. volatile和synchronized实现内存可见性的区别

    先看看synchronized实现内存可见性 加锁(synchronized同步)的功能不仅仅局限于互斥行为,同时还存在另外一个重要的方面:内存可见性.我们不仅希望防止某个线程正在使用对象状态而另一个 ...

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

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

  4. java多线程之内存可见性-synchronized、volatile

    1.JMM:Java Memory Model(Java内存模型) 关于synchronized的两条规定: 1.线程解锁前,必须把共享变量的最新值刷新到主内存中 2.线程加锁时,将清空工作内存中共享 ...

  5. Java多线程之内存可见性和原子性:Synchronized和Volatile的比较

    Java多线程之内存可见性和原子性:Synchronized和Volatile的比较     [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article ...

  6. 原子性、内存可见性和重排序——重新认识synchronized和volatile

    一.原子性 原子性操作指相应的操作是单一不可分割的操作.例如,对int变量count执行count++d操作就不是原子性操作.因为count++实际上可以分解为3个操作:(1)读取变量count的当前 ...

  7. 10-Java中共享内存可见性以及synchronized和volatile关键字

    Java中共享变量的内存可见性 我们首先来看一下在多线程下处理共享变量时Java的内存模型,如图所示 Java内存模型规定,将所有的变量都存放在主存中,当线程使用变量的时候,会把主内存里面的变量赋值到 ...

  8. JVM并发机制的探讨——内存模型、内存可见性和指令重排序

    并发本来就是个有意思的问题,尤其是现在又流行这么一句话:“高帅富加机器,穷矮搓搞优化”. 从这句话可以看到,无论是高帅富还是穷矮搓都需要深入理解并发编程,高帅富加多了机器,需要协调多台机器或者多个CP ...

  9. 二、volatile关键字 - 内存可见性

    1.内存可见性 ​ (程序在运行时,jvm会为每一个执行任务的线程都分配一个独立的缓存,用于提高效率) ​ 我觉得可以这样来理解: ​ 内存:啥是内存?就是可以理解成电脑当中的内存条,程序创建个变量, ...

随机推荐

  1. JQuery 网页瞄点

    $("html,body").animate({ scrollTop: $("#Content1").offset().top }, 3000); 代码说明:h ...

  2. 平衡树Treap模板与原理

    这次我们来讲一讲Treap(splay以后再更) 平衡树是一种排序二叉树(或二叉搜索树),所以排序二叉树可以迅速地判断两个值的大小,当然操作肯定不止那么多(不然我们还学什么). 而平衡树在排序二叉树的 ...

  3. p2p 打洞技术

    根据通信双方所处网络环境不同,点对点通信可以划分成以下三类:i> 公网:公网ii>公网:内网iii>内网:内网前两种容易实现,我们这里主要讨论第三种.这其中会涉及到NAT和NAPT的 ...

  4. OLEDB数据源

    title: OLEDB数据源 date: 2018-01-12 21:42:37 tags: [OLEDB, 数据库编程, VC++, 数据库] categories: windows 数据库编程 ...

  5. git命令记录

    1, clone 远程分支 git clone 命令默认的只会建立master分支,如果你想clone指定的某一远程分支(如:dev)的话,可以如下: A. 查看所有分支(包括隐藏的)  git br ...

  6. sublime 挪移的第一层(插件的安装和使用)

    人总要坚持一些什么,故从今儿始,咱也开始写博客,不为成为大神,不为成为分享大师,只为让自己快活有成就感一些 1.sublime的安装 登录sublime官网 : http://www.sublimet ...

  7. css的常用效果总结

    1.模糊遮罩效率,模糊滤镜效果 -webkit-filter: blur(3px); -moz-filter: blur(3px); -o-filter: blur(3px); -ms-filter: ...

  8. 5.移植uboot-设置默认环境变量,裁剪,并分区

    在上一章,我们使用网卡传输文件,每次启机时,环境变量都要变为默认值,需要重新设置ip,MAC地址才行,由于没有配置mtdparts命令,启动内核也不成功 所以本章主要学习: 1)修改环境变量默认值 2 ...

  9. MATLAB数字图像处理学习笔记

    我们都知道一幅图片就相当于一个二维数组,可以用一个矩阵来表示,而MATLAB可以说就是为矩阵运算而生的,所以学习图像处理,学习MATLAB势在必行! 一. MATLAB基础知识 1. 读取图像 %im ...

  10. LVS集群DR模式实例(4)

    LVS集群DR模式实例 1. 实验拓扑图 2. 实验环境 3台CentOS6.4 64bit的服务器. 类型 IP DR eth0:10.20.73.20  VIP eth0:0 10.20.73.3 ...