【回顾】对volatile关键字的简单理解(1)

本文探讨了Java内存模型在多线程环境中的问题,以一个简单的increment方法为例,展示了并发执行可能导致的数据不一致。分析了内存模型的原子性、可见性和有序性,并解释了volatile关键字如何确保可见性和有序性,但无法保证原子性。通过volatile修饰变量,可以避免线程间的数据延迟,提升并发安全性。
摘要由CSDN通过智能技术生成

一、Java内存模型

比如有一个类:

public class HelloWorld{
	private int data = 0;
	public void increment(){
		data++;
	}
}
HelloWorld helloWorld = new HelloWorld();//对象放在堆内存中,包含实例变量

假设有2个线程,去调用同一个helloWorld对象的increment方法:
//线程1

new Thread(){
	public void run(){
		helloWorld.increment();
	}
}.start();
//线程2
new Thread(){
	public void run(){
		helloWorld.increment();
	}
}.start();

按理来说,两个线程执行完之后,data的值应该变成2,但是实际情况data很可能不为2,出现了并发执行的问题。

要找出这个问题的原因,要从Java内存模型的角度来分析两个线程执行时的Java内存情况:
在这里插入图片描述

1、假设data变量存放在主内存中;
2、每个线程在运行时都会有自己私有的线程工作内存,对应CPU级别的缓存;
3、2个线程对data去进行+1,都要先进行read,拿到data的值;
4、随后要把拿到的值load到线程的工作内存中;
5、线程在工作内存中use和assign完之后,把修改后的值store出来;
6、线程最后才把修改后的data值写回主内存中。
从以上的步骤很明显可以看出,由于两个线程在并发地执行,所以竞争cpu时间片的过程中很可能发生问题:如两个线程同时read出data的值为0。

二、内存模型中的原子性、可见性、有序性

(1)可见性
如上图中,主内存中的data值为0,线程1和线程2 read到的值都为0,当线程1先执行完increment操作,把data值改为1并写回到了主内存中,线程2能立马得到data=1的信息,则为具备可见性;
(2)原子性
如上图中,如果线程1在对data进行加1的过程中,线程2无法同时进行操作,则具备原子性;
(3)有序性
如以下一段代码:

flag = false;
//线程1
prepare();//准备资源
flag = true;

//线程2
while(!flag){
thread.sleep(1000);
}
execute();//执行操作

编译器和指令器,有时候为了提高代码执行效率,会将指令重排序,比如将flag=true排到prepare()之前去执行了。
如果具备有序性,则不会发生指令重排。

三、volatile关键字的原理

用来解决可见性和有序性,不能保证原子性;
如上图中的data,如果给data加上volatile,变成
private volatile int data = 0;
data就具备了可见性,当线程1和线程2同时把data=0的值加载到工作内存中,假设线程1先执行完了,将data=1写回主内存,此时线程2能同时得到data被改为了1的信息,失效掉之前data=0的信息,从而保证了并发操作的安全性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值