所有對象都自動含有單一的鎖,也就是所有對象都有且只有唯一的鎖,所以當某個任務(線程)訪問一個類A中含有sycnhronized的方法是,那麼在這個線程從該方法返回之前(也就是該方法在當前線程執行完之前),類A中的其他被該關鍵字標記的方法在其他的線程中都會被阻塞。
通俗點說就是,當調用A的含有synchronized的方法是,A會被枷鎖,此時A中其他含有synchronized方法只有等到前一個方法調用完畢釋放了鎖才能被調用
具體說明見另一篇部落格<<java同步和互斥》
下面看看具體的程式,其中,Timer中的欄位num是共用資源
public class TestSync implements Runnable { Timer timer = new Timer(); public static void main(String[] args) { TestSync test = new TestSync(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } public void run(){ timer.add(Thread.currentThread().getName()); }}class Timer{ private static int num = 0; public void add(String name){ num ++; try {Thread.sleep(1);} catch (InterruptedException e) {} System.out.println(name+", 你是第"+num+"個使用timer的線程"); }}
由於沒有同步,所以運行結果如下所示
t1, 你是第2個使用timer的線程
t2, 你是第2個使用timer的線程
也就是說當線程一運行到num++的時候被打線程2打斷了,由於java中遞增和遞減操作均不是原子操作,所以本程式中即使沒有調用sleep,也會出現這種被打斷的情況
下面看看同步的效果
public void add(String name) { synchronized (this) {//同步 num++; try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println(name + ", 你是第" + num + "個使用timer的線程"); } }
這樣運行結果就會出是正確的
t1, 你是第1個使用timer的線程
t2, 你是第2個使用timer的線程
但是,下面為了說明問題把TestSync裡面的run方法改成如下所示
public void run() {// time.add(Thread.currentThread().getName()); try { Thread.sleep(1000);//為了顯示結果,讓其睡眠一秒 } catch (InterruptedException ex) { Logger.getLogger(TestSync.class.getName()).log(Level.SEVERE, null, ex); } System.out.println(Thread.currentThread().getName() + "----"); }
那麼在此運行就是
t1, 你是第1個使用timer的線程
t2, 你是第2個使用timer的線程
t1--------
t2--------
而不是你所想象的
t1, 你是第1個使用timer的線程
t1----
t2, 你是第2個使用timer的線程
t2----
原因就是線上程t1在睡眠的時候,線程t2切換進來,執行了一次,怎樣得到正確的結果呢,下面把TestSync裡面的run方法做如下改進就可以得到上面預期的結果
public void run() { synchronized(time){ time.add(Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException ex) { Logger.getLogger(TestSync.class.getName()).log(Level.SEVERE, null, ex); } System.out.println(Thread.currentThread().getName() + "----"); } }
因為t1先獲得time的鎖,所以在執行完run裡面的同步塊之前,即使sleep(),t2也不會執行,因為t2沒有獲得time的鎖,且sleep()操作也不釋放鎖(這也是和wait的巨大區別)