java 线程的基本知识(四)——volatile

本文深入探讨了Java中的volatile关键字,解析其工作原理,包括确保变量可见性、禁止指令重排序及不保证原子性的特点,并提供了典型应用场景与替代方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

volatile

  1. 当写一个volatile变量时,jvm会把该线程对应的本地内存中的变量强制刷新到主内存中去;
  2. 这个写操作会导致其他线程中的缓存无效。

禁止指令重排序优化

重排序:

  • 编译器优化的重排序(编译器优化)
  • 指令级并行重排序(处理器优化)
  • 内存系统的重排序(处理器优化)
  1. 当第二个操作是voaltile写时,无论第一个操作是什么,都不能进行重排序
  2. 当第一个操作是volatile读时,不管第二个操作是什么,都不能进行重排序
  3. 当第一个操作是volatile写时,第二个操作是volatile读时,不能进行重排序

volatile不能保证原子性

原子性:程序要么完整的被执行,要么完全不执行。

例子:

public class volatileTest {
	private volatile int number=0;
	public int getNumber() {
		return this.number;
	}
	public  void add() {
		this.number++;
	}
	public static void main(String[] args) {
		volatileTest v=new volatileTest();
		for(int i=0;i<500;i++) {
			new Thread(new Runnable() {
				public void run(){
					v.add();
				}
			}).start();;
		}
		//如果还有子线程在运行,主线程就让出CPU资源,直到所有的子线程都运行完,主线程再继续往下执行
		while(Thread.activeCount()>1) {
			Thread.yield();
		}
		System.out.println("number:"+v.getNumber());
	}
}

运行结果:

number:500

number:499

number:498

为啥运行结果会不同呢???不应该都是500吗???

在本示例中,用volatile来保证了number变量在各线程之间的可见性,按照volatile的作用的确应该输出500。但是由于num++这个操作不具备原子性,它包含了三个操作:1)从内存中读取num的值;2)把num的值进行+1操作;3)把修改后的num重新写会内存中。各线程通过volatile无法保证进行操作的值是当前更新后的值,即可能存在两次+1操作的结果是相同的。

解决方案:

  • 使用synchronized关键字
  • 使用ReentrantLock(java.util.concurrent.locks包下)
  • 使用AtomicInterger(java.util.concurrent.atomic包下)

volatile适合场所:

1.对变量的写入操作不依赖其当前值

  • 不满足:num++,count=count*8等;
  • 满足:boolean变量,记录温度变化的变量等

2.该变量没有包含在具有其他变量的不变式中

  • 不满足:不变式low<up
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值