volatile的用法详解

1. volatile的作用

  1. 保证了可见性
  2. 不保证原子性
  3. 防止指令重排

2. 可见性的验证

class MyData{
	volatile int number = 0;
	public void addTo60(){
		thie.number = 60;
	}
}
public class VolatileDemo{
	public static void main(String[] args) {
		MyData myData = new MyData();
		new Thread(()->{
			try{TimeUnit.SECONDS.sleep(3);}catch(InterruptedException e){e.printStackTrace();}
			myData.addTo60();
		}, "AAA").start();
		while(myData.number == 0) {
			// 不加volatile永远进行这个循环,因为上边线程改变值,main线程是不知道的
		}
	}
}

3. 不保证原子性的验证

class MyData{
	volatile int number = 0;
	public void addTo60(){
		thie.number = 60;
	}
	public void addPlusPlus(){
		number++;
	}
	AtomicInteger atomicInteger = new AtomicInteger();
	public void addMyAtomic(){
		atomicInteger.getAndIncrement();
	}
}
public class VolatileDemo{
	public static void main(String[] args) {
		MyData myData = new MyData();
		for(int i = 0; i < 20; i++) {
			new Thread(() -> {
				for(int j = 1; j <= 1000; j++){
					myData.addPlusPlus();
					myData.addMyAtomic();
				}
			}, String.valueOf(i)).start();
		}
		// 让上边的线程执行完做往后执行
		while(Thread.activeCount() > 2) {
			Thead.yield();
		}
		System.out.println(myData.number); // volatile不保证原子性,所以最后的值不是20000
		System.out.println(myData.atomicInteger ); // 可以保证原子性,值是20000
	}
}

4. 防止指令重排

  1. 指令重排概念:计算机在执行程序时,为了提高性能,编译器和处理器会对指令做重排。
    源代码 ----> 编译器优化的重排 ----> 指令并行的重排 ----> 内存系统的重排 ----> 最终执行的指令
  2. 案例1:
// 多线程情况下会出现指令重排,可能是1234,2134,1324
public void mySort(){
	int x = 11; //语句1
	int y = 12; //语句2
	x = x + 5;  // 语句3
	y = x * x;  // 语句4
}
  1. 案例2:
int a, b, x, y = 0;
x = a; b = 1; //线程1
y = b; a = 2; //线程2
// x = 0, y = 0
//指令重排后可能的执行:
b = 1; x = a; //线程1
a = 2; y = b; //线程2
// x= 2 y = 1

你可能感兴趣的:(java,java)