synchronized和volatile解决线程可见性

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值