volatile探究

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.在访问变量时候不需要加锁

发布了137 篇原创文章 · 获赞 123 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/lz20120808/article/details/97019834
今日推荐