测试代码如下:
1 public class SyncNonFinalField { 2 private Object object = new Object(); 3 public void start() { 4 5 new Thread(new Runnable() { 6 7 @Override 8 public void run() { 9 System.out.println("线程1等待锁"); 10 synchronized (object) { 11 System.out.println("线程1获取到了锁"); 12 object = new Object(); 13 System.out.println("线程1准备sleep"); 14 try { 15 Thread.sleep(5000L); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 System.out.println("线程1 sleep结束"); 20 } 21 } 22 }).start(); 23 24 new Thread(new Runnable() { 25 26 @Override 27 public void run() { 28 try { 29 Thread.sleep(1000L); 30 } catch (InterruptedException e) { 31 e.printStackTrace(); 32 } 33 System.out.println("线程2等待锁"); 34 synchronized (object) { 35 System.out.println("线程2获取到了锁"); 36 } 37 } 38 }).start(); 39 } 40 }
输出结果:
线程1等待锁
线程1获取到了锁
线程1准备sleep
线程2等待锁
线程2获取到了锁
线程1 sleep结束
也就是说,在线程1的同步块中,只要一给object重新赋值,线程2立即就进入到了同步块中。
分析:
synchronized (object) 锁的是object这个引用所指向的对象,而不是锁引用。
线程1 sleep 时,并没有释放旧对象的锁。
但它让 object 指向了一个新对象之后,线程2就立即获取到了新对象的锁,所以线程2进入到了同步块中。
解决办法:
用final修饰object,避免它在同步块中被重新赋值。