測試代碼如下:
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,避免它在同步塊中被重新賦值。