博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
synchronized和volatile解决线程可见性
阅读量:6884 次
发布时间:2019-06-27

本文共 2010 字,大约阅读时间需要 6 分钟。

hot3.png

java多线程开发中,控制共享数据比较麻烦,有可见性和同步性。一般控制可见性我们可以通过synchronized和volatile控制,而同步性我们只能通过synchronized或Lock来控制。

我喜欢通过对一个问题的理解,来理解某个知识点,因为我觉得知识就是为了解决问题。

public class SynchronizedTest {		private static boolean flag = true;		public static void main(String[] args) throws Exception{		new Thread(new Runnable() {			@Override			public void run() {				while(flag) {									}				System.out.println("子线程执行结束================");			}		}).start();				Thread.sleep(1000);				flag = false; // 关闭线程输出		System.out.println("flag已被修改为false");	}}

上面的代码在64位JVM机器(准确来说应该是jvm的server模式)上执行一般都不会输出“子线程执行结束================”,而在32位的jvm的client模式下却可以输出“子线程执行结束================”。

通过 java -version 可以查看自己本机jvm运行的模式。

注意:如果你的jvm装的是64位,那么是无法切换到client模式的。

上面的问题引起的原因其实就是由于java的内存模型引起的,java内存模型中分有主内存和工作内存之分,主内存可以理解为共享数据的区域(不知道准确不准确),而工作内存(主内存数据的副本)是每个线程私有的一块区域,每个线程对共享数据的修改,不会直接操作共享数据,一般是先修改工作内存中的数据,然后在某个特定的时候刷新到主存,其他线程才有机会看到其修改。

解决以上问题

  1. 可以将flag前加上volatile关键字。

private static volatile boolean flag = true;

原因:可以这么简单理解,加了volatile关键字的共享变量,所有线程对其值的获取或修改都直接通过主存,绕过来工作内存,所以主线程对flag的修改子线程马上就可以看到。

volatile的原理就是加内存屏障,所有的读都在写之后,这样保证了子线程对flag的访问在主线程对flag的修改之后。

  1. 可以使用synchronized
new Thread(new Runnable() {	@Override	public void run() {		int count = 0;		while(flag) {			synchronized(SynchronizedTest.class){ //这里可以锁任何共享对象				count++;			}				}		System.out.println("子线程执行结束================");	}}).start();

上面之所以加上 count ,是防止JIT优化将无用的锁代码块优化掉。

原因:由于jvm规定在进入synchronized之前会将所有其他线程的工作内存刷新到主存(这个我不太确定是否正确,如果有确定的请留言告诉我,谢谢),在离开synchronized块之前会将本线程的工作内存刷新到主内存。

在验证这个之中我踩过一个坑,我的代码如下:

new Thread(new Runnable() {	@Override	public void run() {		while(flag) {			System.out.println("====falg=====");			}		System.out.println("子线程执行结束================");	}}).start();

这样在任何情况下都可以输出"子线程执行结束================",找了很久才发现原来 System.out.println 方法中有synchronized同步块导致。

这个方法来自 System.out

public void println(String x) {        synchronized (this) {            print(x);            newLine();        }    }

之中有不少观点是我自己的观点,可能不准确,或是错误的,希望你们能给我指出,谢谢。

转载于:https://my.oschina.net/aiheng1988/blog/526950

你可能感兴趣的文章
小知识~VS2012的xamarin加载失败解决
查看>>
从Linux 访问Windows的文件夹和Windows的共享打印机
查看>>
Delphi 日期格式问题 - is not a valid date
查看>>
nodejs express 启动报错:Error: Cannot find module 'xxx'
查看>>
10.7. glusterfs
查看>>
GET和POST的区别及get和post关于请求的编解码的问题
查看>>
BZOJ 1968: [Ahoi2005]COMMON 约数研究(新生必做的水题)
查看>>
记录mysql性能查询过程
查看>>
Appium 服务关键字
查看>>
线程安全日期格式化操作的几种方式
查看>>
android XMl 解析神奇xstream 六: 把集合list 转化为 XML文档
查看>>
增加eclipse启动的Tomcat内存的
查看>>
彻底厘清真实世界中的分布式系统
查看>>
HybridDB for PostgreSQL上手指南
查看>>
『0013』 - Solidity Types - 固定大小字节数组(Fixed-size byte arrays)
查看>>
java-并发-ConcurrentHashMap高并发机制-jdk1.6
查看>>
Kubernetes之服务质量保证(QoS)
查看>>
大数据业务应用场景
查看>>
低带宽DDoS攻击可瘫痪防火墙
查看>>
IBM云计算扩展在华Bluemix生态系统
查看>>