1. 程式人生 > >透徹理解 Java synchronized 物件鎖和類鎖的區別

透徹理解 Java synchronized 物件鎖和類鎖的區別

synchronized 加到 static 方法前面是給class 加鎖,即類鎖;而synchronized 加到非靜態方法前面是給物件上鎖。這兩者的區別我用程式碼來演示下:

  1. 物件鎖和類鎖是不同的鎖,所以多個執行緒同時執行這2個不同鎖的方法時,是非同步的。

在Task2 中定義三個方法 doLongTimeTaskA和doLongTimeTaskB是類鎖,而doLongTimeTaskC是物件鎖。

public class Task2 {

    public synchronized static void doLongTimeTaskA() {
        System.out
.println("name = " + Thread.currentThread().getName() + ", begain"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("name = " + Thread.currentThread().getName() + ", end"); } public
synchronized static void doLongTimeTaskB() { System.out.println("name = " + Thread.currentThread().getName() + ", begain"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("name = " + Thread.currentThread().getName() + ", end"
); } public synchronized void doLongTimeTaskC() { System.out.println("name = " + Thread.currentThread().getName() + ", begain"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("name = " + Thread.currentThread().getName() + ", end"); }

三個執行緒的程式碼如下:

class ThreadA extends Thread{

    private Task2 mTask2;

    public ThreadA(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskA();
    }
}

class ThreadB extends Thread{

    private Task2 mTask2;

    public ThreadB(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskB();
    }
}

class ThreadC extends Thread{

    private Task2 mTask2;

    public ThreadC(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskC();
    }
}

main函式中執行程式碼如下:

        Task2 mTask2 = new Task2();
        ThreadA ta = new ThreadA(mTask2);
        ThreadB tb = new ThreadB(mTask2);
        ThreadC tc = new ThreadC(mTask2);

        ta.setName("A");
        tb.setName("B");
        tc.setName("C");

        ta.start();
        tb.start();
        tc.start();

執行的結果如下:

name = A, begain, time = 1487311199783
name = C, begain, time = 1487311199783
name = C, end, time = 1487311200784
name = A, end, time = 1487311200784
name = B, begain, time = 1487311200784
name = B, end, time = 1487311201784

可以看出由於 doLongTimeTaskA和doLongTimeTaskB都是類鎖,即同一個鎖,所以 A和B是按順序執行,即同步的。而C是物件鎖,和A/B不是同一種鎖,所以C和A、B是 非同步執行的。(A、B、C代指上面的3中方法)。

我們知道物件鎖要想保持同步執行,那麼鎖住的必須是同一個物件。下面就修改下上面的來證明:

Task2.java不變,修改ThreadA 和 ThreadB 如下:

class ThreadA extends Thread{

    private Task2 mTask2;

    public ThreadA(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskC();
    }
}

class ThreadB extends Thread{

    private Task2 mTask2;

    public ThreadB(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        mTask2.doLongTimeTaskC();
    }
}

main方法如下:

 Task2 mTaska = new Task2();
 Task2 mTaskb = new Task2();
 ThreadA ta = new ThreadA(mTaska );
 ThreadB tb = new ThreadB(mTaskb );


 ta.setName("A");
 tb.setName("B");

 ta.start();
 tb.start();

結果如下:

name = A, begain, time = 1487311905775
name = B, begain, time = 1487311905775
name = B, end, time = 1487311906775
name = A, end, time = 1487311906775

從結果看來,物件鎖鎖的物件不一樣,分別是mTaska , mTaskb,所以執行緒A和執行緒B呼叫 doLongTimeTaskC 是非同步執行的。

但是,類鎖可以對類的所有物件的例項起作用。只需修改ThradA
和 ThreadB,main 方法不做改變,修改如下:

class ThreadA extends Thread{

    private Task2 mTask2;

    public ThreadA(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
        //mTask2.doLongTimeTaskC();
        mTask2.doLongTimeTaskA();
    }
}

class ThreadB extends Thread{

    private Task2 mTask2;

    public ThreadB(Task2 tk){
        mTask2 = tk;
    }

    public void run() {
       //mTask2.doLongTimeTaskC();
        mTask2.doLongTimeTaskA();
    }
}

結果如下:

name = A, begain, time = 1487312239674
name = A, end, time = 1487312240674
name = B, begain, time = 1487312240674
name = B, end, time = 1487312241674

可以看出 線上程A執行完doLongTimeTaskA方法後,執行緒B才會獲得該類鎖接著去執行doLongTimeTaskA。也就是說,類鎖對所有的該類物件都能起作用。

總結:
1. 如果多執行緒同時訪問同一類的 類鎖(synchronized 修飾的靜態方法)以及物件鎖(synchronized 修飾的非靜態方法)這兩個方法執行是非同步的,原因:類鎖和物件鎖是2中不同的鎖。
2. 類鎖對該類的所有物件都能起作用,而物件鎖不能。