虽然已经工作了半年了...虽然一直是在做web开发....但是平时一直很少使用多线程.....
然后最近一直在看相关知识..所以就有了这篇文章
用例子来说明问题吧
1 public class VolatileTest { 2 boolean b = false; 3 int a = 0; 4 5 public static void main(String[] args) { 6 for (int i=0; i<1000000; i++) { 7 VolatileTest resource = new VolatileTest(); 8 Thread t1 = new Thread(new A(resource)); 9 Thread t2 = new Thread(new B(resource)); 10 t1.start(); 11 t2.start(); 12 //System.out.println(i); 13 } 14 } 15 } 16 17 class A implements Runnable { 18 VolatileTest r = null; 19 20 public A(VolatileTest a) { 21 r = a; 22 } 23 24 public void run() { 25 r.a = 1; 26 r.b = true; 27 } 28 29 } 30 31 class B implements Runnable { 32 VolatileTest r = null; 33 34 public B(VolatileTest a) { 35 r = a; 36 } 37 38 public void run() { 39 while (!r.b) { 40 Thread.yield(); 41 } 42 int temp = r.a; 43 if (temp == 0) { 44 System.out.println("出现CPU指令重排"); 45 } 46 } 47 }
在这个例子中我总共输出了11次出现CPU指令重排
之所以会有这个输出,是因为虽然在A类里代码是先将r.a设置为1,再修改r.b为true.(Line:25,26)但是在CPU执行的时候仍然可能会先做第26行代码,再做25行代码..因为这2行代码之间没有什么关联.所以优化的时候可能会改变顺序.这样就导致了会输出出现CPU指令重排
如果将VolatileTest 的b属性增加volatile关键字的话就不会有这个情况.它可以保证这个对象前面的操作与后面的操作的顺序不会相互调换.相当于把原本代码分成了2段(A类的run方法中25行之前的代码是一段,25行之后的代码是一段,只是这里run里的代码比较少),前后2段代码顺序不会调换,但是这2段代码自己内部之间的顺序还是可以调换的.
这种问题在单线程下是不会出现的,因为如果前后2句代码之间有关联,那么CPU会保证前一句代码先于后一句代码执行.但是在多线程里得不到保证. 所以才需要volatile关键字
我现在对volatile的理解就是它像是synchronized的弱化版本.它可以阻止一些情况下的并发问题,但是另外一些情况的并发问题是阻止不了的.(后面会写哪些情况可以哪些不可以)