Java并发Java Volatile使用

线程并发的时候, 通常会遇到下面三种问题

1)原子性

2)可见性

3)有序性

在阅读这篇文章时,需要先了解以上三种概念,这里不做详细的说明,网上有各种说明,在此略过:

Volatile两大作用

1 .可见性

2. 防止重排(保证有序性)

 

1.  Volatile可见性

直接用一段代码来看看并行会出现的问题

package com.yaya.java.volatileTest;

public class VolatileTest {
	volatile int a = 1;
	volatile int b = 2;

	public void change() {
		a = 3;
		b = a;
	}

	public void print() {
		if ((b == 3) && (a != 3)) {
			System.out.println("b=" + b + ";a=" + a);
		}

	}

	public static void main(String[] args) {
		while (true) {
			final VolatileTest test = new VolatileTest();
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					test.change();
				}
			}).start();

			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					test.print();
				}
			}).start();

		}
	}
}

 

看看结果诡异不

 

不考虑线程安全, a b 的值有下面几种可能  

1. change先执行  结果为 a = 3, b =3

2. change 后执行, 结果为a=1, b=2

3. 交叉执行, 结果为 a= 3, b=2 

但既然有个条件

 

为什么会打印  b=3, a=3 或者b=3, a=1的情况呢,这是因为上面的两个线程之间,没有实现可见性

 

if((b == 3) && (a !=3)  还会打印a=3, b=3 是因为 运行到 if((b == 3) && (a !=3)时,是因为 线程1运行完以后 自己工作内存中a=3, b=3但是并不一定什么时候写入物理内存中, 恰巧这个时候b=3写入物理内存,a=3没写入,所以物理内存 a=1 但是system.out.pritnln是, a=3已经写入物理内存,所以会出现a=3.b=3这样的结果

 

b=3, a=1  又是怎么出现的呢? 是因为 线程1运行完以后 内存中a=3, b=3但是并不一定什么时候写入物理内存中, 恰巧线程二读取的时候 b=3写入物理内存,a=3没写入,也就是说线程1修改的内存对于线程2并不可见, 可见性没有实现就是这个意思。

那这么确保可见性呢,用synchronized可以 但是synchronized只能修饰一个对象, 或者方法, 对于一个变量说来可以使用volatile, 因为使用volatile修饰以后就会强制在复制以后立即从工作内存中写入物理内存,不会出现上面这种诡异的现象了

 

2. Volatile的防止重排

对于指令重排这里只做简单的介绍: 

指令重排序,一般来说,对于单线程来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的,但是并发下并不能保证最后的结果,使用Volatile禁止指令重排

volatile的原理和实现机制

 

下面这段话摘自《深入理解Java虚拟机》:

“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”

 Volatile并不能保证原子性,常见的原子性可以通过以下两种方式理解:

1) a++ 并不是一步就可以实现, 它分为以下几步:读取a的值 在a的值上+1, 将a的值写入主内存(物理内存)

2)b=a  和上面的a++理解一样, 读取a, 将a的值赋给b , 写入主内存(物理内存)

Volatile只能保证读取的时候从主内存中读取, 但是在+1的时候, 如果主内存的值变化, 就会造成数据的不一致, 所以不能保证数据的原子性,为了确保数据的一致性,Java 中提供Atomic包来实现原子性, 或者说Atomic是在Volatile的基础上, 再使用CAS方式实现了线程安全。关于Atomic包会在后续文章中将详细说明

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值