提出两个观点:
1.Boolean对象赋值为true和false,会改变指向的对象.
测试代码:
private volatile Boolean isTrue = Boolean.FALSE; //此处用false也一样 public void aMethod() { for (int i = 0; i < 10; i++) { Thread t = new Thread() { public void run() { synchronized (isTrue) { isTrue = !isTrue; System.out.println(Thread.currentThread().getName() + " - isTrue=" + isTrue); try { Double ran = 1000 * Math.random(); Thread.sleep(ran.intValue()); } catch (InterruptedException e) { } if (!isTrue) System.out.println(Thread.currentThread().getName() + " - Oh, No!"); isTrue = !isTrue; System.out.println(Thread.currentThread().getName() + " exit"); } } }; t.start(); } }
其中一次的运行结果:
Thread-0 - isTrue=true Thread-1 - isTrue=false Thread-0 - Oh, No! Thread-0 exit Thread-9 - isTrue=false Thread-1 - Oh, No! Thread-1 exit Thread-9 exit Thread-8 - isTrue=true Thread-8 exit Thread-7 - isTrue=true Thread-7 exit Thread-6 - isTrue=true Thread-6 exit Thread-5 - isTrue=true Thread-5 exit Thread-4 - isTrue=true Thread-4 exit Thread-3 - isTrue=true Thread-3 exit Thread-2 - isTrue=true Thread-2 exit
按照一般的逻辑,isTrue作为共享对象被10个线程共享,每个线程的run方法代码都被isTrue加锁,应该是线程安全的,不应该输出Oh, No!;但是输出结果却刚好相反;
分析原因:isTrue初始化被赋值为Boolean.false这个常量,进入run时,synchronized锁的是Boolean.false这个对象,后取非,isTrue指向发生改变,指向Boolean.true这个常量,后面线程进入run后synchronized锁的Boolean.true这个对象,因此不会发生线程互斥,输出Oh, No!,达不到线程安全目的。
2.以为同步的是不同对象,实际是一个对象。
可能的输出:import java.util.Random; public class BooleanTest { private volatile Boolean aBoolean = false;//Boolean.FALSE; private volatile Boolean anotherBoolean = false; private long t1 = System.currentTimeMillis(); public void aMethod2() { final Random random = new Random(); for (int i = 0; i < 10; i++) { Thread t = new Thread() { public void run() { int val = random.nextInt(); if (val % 2 == 0) { synchronized (aBoolean) { System.out.println(Thread.currentThread().getName() + " aBoolean_costMills:"+(System.currentTimeMillis() -t1)); t1 = System.currentTimeMillis(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } else { synchronized (anotherBoolean) { System.out.println(Thread.currentThread().getName() + " anotherBoolean_costMills:"+(System.currentTimeMillis() -t1)); t1 = System.currentTimeMillis(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; t.start(); } } public static void main(String... args) { BooleanTest bt = new BooleanTest(); bt.aMethod2(); } }
Thread-0 anotherBoolean_costMills:0 Thread-9 aBoolean_costMills:204 Thread-8 aBoolean_costMills:110 Thread-7 aBoolean_costMills:109 Thread-6 anotherBoolean_costMills:110 Thread-5 anotherBoolean_costMills:204 Thread-4 aBoolean_costMills:219 Thread-1 aBoolean_costMills:110 Thread-3 aBoolean_costMills:109 Thread-2 aBoolean_costMills:110
分析原因:明明程序加了两个锁aBoolean,anotherBoolean,执行这两个锁里面的代码应该是互不干扰,但是打印却显示:如果先前执行的是aBoolean,后面执行的是anotherBoolean,需要等待110ms(正常应该是不等待);如果先前anotherBoolean,后面aBoolean,等待了204ms(正常应该是不等待);这说明这两者执行发生了互斥;原因是aBoolean和anotherBoolean指向了同一对象Boolean.false;其实Boolean.FALSE和false指向的是同一个对象,因此aBoolean赋值Boolean.false还是false,效果一样。
综上:尽量不用使用Boolean对象作为被同步对象,不然可能会出现意想不到的问题,或者对以后的代码修改造成陷阱。