volatile探究

本文详细介绍了Java中的volatile关键字,包括其作用原理、使用方法及注意事项。解释了如何利用volatile保证多线程环境下变量的可见性,并探讨了其在禁止指令重排序方面的特性。同时,文章也指出了volatile无法保证操作原子性的局限性。

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

1.为什么使用volatile

深入理解Java虚拟机中提到过jvm内存模型。在多线程操作中,普通变量在线程间传递均需要通过主内存实现。正常情况下,每个线程在主内存中获取变量,放到自己的工作内存中,然后读写变量值。线程完成后将变量放入主内存。不同线程之间的变量不能直接访问。所以线程不能及时获取其他线程对同一变量修改的值。造成变量不可见。所以为了确保某个线程写入的变量对于其他线程来说是可见的,需要加同步。
在这里地方

2.volatile使用

修饰类变量 :volatile boolean b = true;

优点

1.保证了变量在线程之间的可见性
有了volatile的修饰,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。变量不会被缓存到寄存器或者对其他处理器不可见的地方。
2.禁止指令重排序优化
赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置)。

缺点

不能保证操作的原子性。

	volatile int count = 0;
	/*synchronized*/ void m(){
		for(int i = 0; i < 10000; i++){
			System.out.println(Thread.currentThread().getName() + " - " + i);
			count++;
		}
	}
	
	public static void main(String[] args) {
		final Test_10 t = new Test_10();
		List<Thread> threads = new ArrayList<>();
		for(int i = 0; i < 10; i++){
			threads.add(new Thread(new Runnable() {
				@Override
				public void run() {
					t.m();
				}
			}));
		}
		for(Thread thread : threads){
			thread.start();
		}
		for(Thread thread : threads){
			try {
			//join() 等线程执行结束,主线程再执行。
				thread.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(t.count);
	}

期望的结果是100000 但实际上会小于100000。
count++; 是可以拆分的,不是一个原子操作,非原子操作都会存在线程安全问题。
解决方法:加锁或者改成原子操作。
即:
1) synchronized (this){
count++;
}
2)
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet();
incrementAndGet()是原子操作。

3.使用情景

1.对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值
2.该变量不会与其他状态变量一起纳入不变性条件中
3.在访问变量时候不需要加锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值