并发编程十:volatile关键字

转载:https://blog.csdn.net/BushQiang/article/details/81349031

当多个线程进行操作共享数据时,可以保证内存中的数据可见。 相较于 synchronized 是一种较为轻量级的同步策略。
缺点:

  • volatile 不具备“互斥性”
  • volatile 不能保证变量的“原子性”

内存可见性是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。
可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的。

我们可以通过同步来保证对象被安全地发布。除此之外我们也可以使用一种更加轻量级的 volatile 变量。

1.例子:
public class TestVolatile {
 
	public static void main(String[] args) {
		ThreadDemo td = new ThreadDemo();
		new Thread(td).start();
		while (true) {
			if (td.isFlag()) {
				System.out.println("------------------进来");
			}
		}
	}
}
 
class ThreadDemo implements Runnable {
	private boolean flag = false;
	@Override
	public void run() {
		// 延迟一秒
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
		}
		flag = true;
		System.out.println("flag=" + isFlag());
	}
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
 
}

flag是共享数据存在于主存中,ThreadDemo启动以后主要是来改变flag的值,而main函数本身也是一个线程来读取flag的值。为了让效果更加明显,在ThreadDemo线程里面run方法里面休眠了1秒,目的是先让flag的值先改变,不然main线程里面可能会先读取到flag的值。按理说ThreadDemo线程先改变了flag的值为true,然后while(true)里面读取flag的值应该是true,然后输出,但是运行结果并不是这样。

运行结果如下:
在这里插入图片描述

2. 为什么会这样呢?在这里插入图片描述

首先ThreadDemo线程先从主存中读取共享数据的内容,然后放入自己的工作内存中,然后再改变flag的值,但是这时的值还没有写回主存中,main就读取的flag的值,此时为false,当ThreadDemo把值写回去后,但是main函数里面的while(true)调用的是系统比较底层的代码,速度快,快到没有时间再去读取主存中的值,所以while(true)读取到的值一直是false。

3. 解决

(1) synchronized锁

while (true) {
			synchronized (td) {
				if (td.isFlag()) {
					System.out.println("------------------进来");
				}
			}
 
		}

synchronized 实际上是对访问修改共享变量的代码块进行加互斥锁,多个线程对synchronized代码块的访问时,某一时刻仅仅有一个线程在访问和修改代码块中的内容(加锁),其他所有的线程等待该线程离开代码块时(释放锁)才有机会进入synchronized代码块。

某一个线程进入synchronized代码块前后,执行过程入如下:
a.线程获得互斥锁
b.清空工作内存
c.从主内存拷贝共享变量最新的值到工作内存成为副本
d.执行代码
e.将修改后的副本的值刷新回主内存中
f.线程释放锁

(2) volatile修饰变量

private volatile boolean flag = false;

volatile如何实现可见性:

volatile变量每次被线程访问时,都强迫线程从主内存中重读该变量的最新值,而当该变量发生修改变化时,也会强迫线程将最新的值刷新回主内存中。这样一来,不同的线程都能及时的看到该变量的最新值。

volatile不能保证变量更改的原子性:

比如sum++,这个操作实际上是三个操作的集合(读取sum,sum加1,将新的值写回sum)
volatile只能保证每一步的操作对所有线程是可见的,但是假如两个线程都需要执行sum++,那么这一共6个操作集合,之间是可能会交叉执行的,那么最后导致sum的结果可能会不是所期望的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值