Java類鎖和對象鎖實踐

jopen 11年前發布 | 43K 次閱讀 Java Java開發

類鎖和對象鎖是否會沖突?對象鎖和私有鎖是否會沖突?通過實例來進行說明。

一、相關約定

為了明確后文的描述,先對本文涉及到的鎖的相關定義作如下約定:

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(); } }</pre>

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.");
}

}</pre>

三、測試結果

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)都是對當前對象加鎖,二者的加鎖方法夠成了競爭關系,同一時刻只能有一個方法能執行。

四、參考資料:

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!