1. 程式人生 > >Java類鎖和物件鎖實踐

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();

	/**
	 * &lt;p&gt;
	 * 無鎖方法
	 *
	 * @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)都是對當前物件加鎖,二者的加鎖方法夠成了競爭關係,同一時刻只能有一個方法能執行。

四、參考資料:


混跡於58同城,對java、開源感興趣。
個人部落格:http://jiehao.iteye.com/

Latest posts by zhaojianhua