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

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

猜你喜欢

转载自blog.csdn.net/ljcgit/article/details/81360605