class Thread2 extends Thread{ public void run() { MyObj obj = new MyObj(); try { obj.sayHello(3000);//3秒 } catch (InterruptedException e) { e.printStackTrace(); } } } class Thread3 extends Thread{ public void run() { MyObj obj = new MyObj(); try { obj.sayHello(1000);//1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } class MyObj {
public synchronized void sayHello(int delay) throws InterruptedException{ Thread.sleep(delay); System.out.println("delay" + delay); } } public class ThreadTest { public static void main(String[] args) throws Exception{ Thread2 t2 = new Thread2(); Thread3 t3 = new Thread3(); t2.start();//先讓t2線程啟動,因為t2要等待3秒,如果線程同步有作用的話t3會處理阻塞狀態 t3.start(); } }
代碼中的MyObj類就是我用于測試的線程安全的對象,它包含了一個sayHello方法,他是帶有synchronized關鍵字的。但測試結果卻是
delay1000
delay3000
這說明關鍵字沒有起作用,也說明了不同的對象實例synchronized方法時是線程不安全的。知道這個結果心里有些難過,只不過事情已經過去就當是學習了經驗吧。
synchronized除了修飾方法外,還可以修飾代碼塊,那就試試看吧,寫了一個新的類:
class MyObj2 { private static Object lockObj = new Object(); public void sayHello(int delay) throws InterruptedException{ synchronized(lockObj){ Thread.sleep(delay); System.out.println("delay" + delay); } } }
用這個類做測試的結果:
delay3000
delay1000
這說明synchronized修改代碼塊時線程同步是起作用的,但這里要注意,采用synchronized代碼塊時,synchronized(lockObj)中的lockObj對象是一個靜態對象,所以他們對應的鎖是同一個,這就可以實現線程間的同步。如果換成synchronized(this)又無法同步。
于是就得想明白為什么會有這兩種差別呢?原來JAVA中每個對象都對應一個鎖,synchronized關鍵字是通過檢查這個對象鎖的狀態來調度的。這下就明白了,原來關鍵就在于對象對應的那個鎖,MyObj之所以不能同步是因為創建了兩不同的對象實例,自然對應的對象鎖就不同,而synchronized修飾的是方法時,其對應檢查的是當前對象的鎖,所以就會出現不同步的情況。
后來在網上查資料的同時也發現一個叫類鎖的東東,通過類鎖獲得的類本身,是唯一的,那么就應該是可以同步了,代碼如下:
class MyObj3 { public synchronized static void sayHello(int delay) throws InterruptedException{ Thread.sleep(delay); System.out.println("delay" + delay); } }
得到的結果
delay3000
delay1000
這說明已經同步了,synchronized+static一起修改時獲得的是類鎖,獲得類本身,所以只有一個,那么同步自然有了效果。
注:“光之子”在評論中指出了本文的一些問題,有興趣的朋友看到本文請把評論也看看吧。
文章列表