Java類鎖和物件鎖實踐
感謝[jiehao]同學的投稿,投稿可將文章傳送到[email protected]
類鎖和物件鎖是否會衝突?物件鎖和私有鎖是否會衝突?通過例項來進行說明。
一、相關約定
為了明確後文的描述,先對本文涉及到的鎖的相關定義作如下約定:
1. 類鎖:在程式碼中的方法上加了static和synchronized的鎖,或者synchronized(xxx.class)的程式碼段,如下文中的increament();
2.物件鎖:在程式碼中的方法上加了synchronized的鎖,或者synchronized(this)的程式碼段,如下文中的synOnMethod()和synInMethod();
3.私有鎖:在類內部宣告一個私有屬性如private Object lock,在需要加鎖的程式碼段synchronized(lock),如下文中的synMethodWithObj()。
二、測試程式碼
1.編寫一個啟動類ObjectLock
public class ObjectLock { public static void main(String[] args) { System.out.println("start time = " + System.currentTimeMillis()+"ms"); LockTestClass test = new LockTestClass(); for (int i = 0; i < 3; i++) { Thread thread = new ObjThread(test, i); thread.start(); } } }
2.編寫一個執行緒類ObjThread,用於啟動同步方法(注意它的run方法可能會調整以進行不同的測試)
public class ObjThread extends Thread { LockTestClass lock; int i = 0; public ObjThread(LockTestClass lock, int i) { this.lock = lock; this.i = i; } public void run() { //無鎖方法 // lock.noSynMethod(this.getId(),this); //物件鎖方法1,採用synchronized synInMethod的方式 lock.synInMethod(); //物件鎖方法2,採用synchronized(this)的方式 // lock.synOnMethod(); //私有鎖方法,採用synchronized(object)的方式 // lock.synMethodWithObj(); //類鎖方法,採用static synchronized increment的方式 LockTestClass.increment(); } }
3.再編寫一個鎖的測試類LockTestClass,包括各種加鎖方法
public class LockTestClass { //用於類鎖計數 private static int i = 0; //私有鎖 private Object object = new Object(); /** * <p> * 無鎖方法 * * @param threadID * @param thread */ public void noSynMethod(long threadID, ObjThread thread) { System.out.println("nosyn: class obj is " + thread + ", threadId is" + threadID); } /** * 物件鎖方法1 */ public synchronized void synOnMethod() { System.out.println("synOnMethod begins" + ", time = " + System.currentTimeMillis() + "ms"); try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synOnMethod ends"); } /** * 物件鎖方法2,採用synchronized (this)來加鎖 */ public void synInMethod() { synchronized (this) { System.out.println("synInMethod begins" + ", time = " + System.currentTimeMillis() + "ms"); try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synInMethod ends"); } } /** * 物件鎖方法3 */ public void synMethodWithObj() { synchronized (object) { System.out.println("synMethodWithObj begins" + ", time = " + System.currentTimeMillis() + "ms"); try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synMethodWithObj ends"); } } /** * 類鎖 */ public static synchronized void increament() { System.out.println("class synchronized. i = " + i + ", time = " + System.currentTimeMillis() + "ms"); i++; try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("class synchronized ends."); } }
三、測試結果
1.測試類鎖和物件鎖,ObjectThread的run方法修改如下:
public void run() { //無鎖方法 // lock.noSynMethod(this.getId(),this); //物件鎖方法1,採用synchronized synInMethod的方式 lock.synInMethod(); //物件鎖方法2,採用synchronized(this)的方式 // lock.synOnMethod(); //私有鎖方法,採用synchronized(object)的方式 // lock.synMethodWithObj(); //類鎖方法,採用static synchronized increment的方式 LockTestClass.increament(); }
終端輸出:
start time = 1413101360231ms synInMethod begins, time = 1413101360233ms synInMethod ends class synchronized. i = 0, time = 1413101362233ms synInMethod begins, time = 1413101362233ms class synchronized ends. synInMethod ends class synchronized. i = 1, time = 1413101364233ms synInMethod begins, time = 1413101364233ms class synchronized ends. synInMethod ends class synchronized. i = 2, time = 1413101366234ms class synchronized ends.
可以看到物件鎖方法(synInMothod)第一次啟動時比類鎖方法(increament)快2秒,這是因為在synInMehtod執行時sleep了2秒再執行的increament,而這兩個方法共用一個執行緒,所以會慢2秒,如果increament在run中放到synInMethod前面,那麼第一次啟動時就是increament快2秒。
而當類鎖方法啟動時,另一個執行緒時的物件鎖方法也幾乎同時啟動,說明二者使用的並非同一個鎖,不會產生競爭。
結論:類鎖和物件鎖不會產生競爭,二者的加鎖方法不會相互影響。
2.私有鎖和物件鎖,ObjectThread的run方法修改如下:
public void run() { //無鎖方法 // lock.noSynMethod(this.getId(),this); //物件鎖方法1,採用synchronized synInMethod的方式 lock.synInMethod(); //物件鎖方法2,採用synchronized(this)的方式 // lock.synOnMethod(); //私有鎖方法,採用synchronized(object)的方式 lock.synMethodWithObj(); //類鎖方法,採用static synchronized increment的方式 // LockTestClass.increament(); }
終端輸出:
start time = 1413121912406ms synInMethod begins, time = 1413121912407ms. synInMethod ends. synMethodWithObj begins, time = 1413121914407ms synInMethod begins, time = 1413121914407ms. synInMethod ends. synMethodWithObj ends synInMethod begins, time = 1413121916407ms. synMethodWithObj begins, time = 1413121916407ms synInMethod ends. synMethodWithObj ends synMethodWithObj begins, time = 1413121918407ms synMethodWithObj ends
和類鎖和物件鎖非常類似。
結論:私有鎖和物件鎖也不會產生競爭,二者的加鎖方法不會相互影響。
3.synchronized直接加在方法上和synchronized(this),ObjectThread的run方法修改如下:public void run() { //無鎖方法 // lock.noSynMethod(this.getId(),this); //物件鎖方法1,採用synchronized synInMethod的方式 lock.synInMethod(); //物件鎖方法2,採用synchronized(this)的方式 lock.synOnMethod(); //私有鎖方法,採用synchronized(object)的方式 // lock.synMethodWithObj(); //類鎖方法,採用static synchronized increment的方式 // LockTestClass.increament(); }
終端輸出:
start time = 1413102913278ms synInMethod begins, time = 1413102913279ms synInMethod ends synInMethod begins, time = 1413102915279ms synInMethod ends synOnMethod begins, time = 1413102917279ms synOnMethod ends synInMethod begins, time = 1413102919279ms synInMethod ends synOnMethod begins, time = 1413102921279ms synOnMethod ends synOnMethod begins, time = 1413102923279ms synOnMethod ends
可以看到,二者嚴格地序列輸出(當然再次執行時先執行synInMethod還是先執行synOnMethod並不是確定的,取決於誰獲得了鎖)。
結論:synchronized直接加在方法上和synchronized(this)都是對當前物件加鎖,二者的加鎖方法夠成了競爭關係,同一時刻只能有一個方法能執行。
四、參考資料:
個人部落格:http://jiehao.iteye.com/