synchronized對象鎖和類鎖.

對象鎖概念

Java的全部對象都含有1個互斥鎖,這個鎖由JVM自動獲取和釋放。線程進入synchronized方法的時候獲取該對象的鎖,固然若是已經有線程獲取了這個對象的鎖,那麼當前線程會等待,synchronized方法正常返回或者拋異常而終止,JVM會自動釋放對象鎖。這裏也體現了用synchronized來加鎖的一個好處,方法拋異常的時候,鎖仍然能夠由JVM來自動釋放。
// 對象鎖:形式1(方法鎖)
    public synchronized void Method1() {
        System.out.println("我是對象鎖也是方法鎖");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    // 對象鎖:形式2(代碼塊形式)
    public void Method2() {
        synchronized (this) {
            System.out.println("我是對象鎖");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

類鎖的概念

因爲一個class不論被實例化多少次,其中的靜態方法和靜態變量在內存中都只有一份。因此,一旦一個靜態的方法被申明爲synchronized。此類全部的實例化對象在調用此方法,共用同一把鎖,咱們稱之爲類鎖。
public synchronized static void Method3() {
        System.out.println("我是類鎖");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
}

代碼演示類鎖和對象鎖

下面這段代碼是兩個類鎖和一個對象鎖,拿到鎖後,睡1秒鐘。
// 類鎖A
    public synchronized static void classLockA() {
        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");
    }

    // 類鎖B
    public synchronized static void classLockB() {
        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 objectLock() {
        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 Test02 test02;
    
    public ThreadA(Test02 tk) {
        test02 = tk;
    }
    
    // 調用類鎖
    public void run() {
        test02.classLockA();
    }
}

class ThreadB extends Thread {
    
    private Test02 test02;
    
    public ThreadB(Test02 tk) {
        test02 = tk;
    }
    
    // 調用類鎖
    public void run() {
        test02.classLockB();
    }
}

class ThreadC extends Thread {
    
    private Test02 test02;
    
    public ThreadC(Test02 tk) {
        test02 = tk;
    }
    
    // 調用對象鎖
    public void run() {
        test02.objectLock();
    }
}
main方法:起了三個線程,共同訪問一個Test02對象
public static void main(String[] args){
        
        Test02 test02 = new Test02();
        ThreadA ta = new ThreadA(test02);
        ThreadB tb = new ThreadB(test02);
        ThreadC tc = new ThreadC(test02);

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

        ta.start();
        tb.start();
        tc.start();
}
執行的結果:
name = A, begain
name = C, begain
name = A, end
name = B, begain
name = C, end
name = B, end
能夠看出因爲 classLockA和classLockB都是類鎖,即同一個鎖,因此 A和B是按順序執行,即同步的。而C是對象鎖,和A/B不是同一種鎖,因此C和A、B是 異步執行的。

分析:java

對象鎖要想保持同步執行,那麼鎖住的必須是同一個對象,舉個例子:

Test02類不變,重起兩個線程類:均對對象鎖進行了調用多線程

class ThreadA extends Thread {
    
    private Test02 test02;
    
    public ThreadA(Test02 tk) {
        test02 = tk;
    }
    
    // 調用類鎖
    public void run() {
        test02.objectLock();
    }
}

class ThreadB extends Thread {
    
    private Test02 test02;
    
    public ThreadB(Test02 tk) {
        test02 = tk;
    }
    
    // 調用類鎖
    public void run() {
        test02.objectLock();
    }
}
main方法:建立兩個不一樣的資源對象,啓動兩個線程,分別對加鎖的方法進行調用
public static void main(String[] args){

        Test02 test02 = new Test02();
        Test02 test03 = new Test02();
        ThreadA ta = new ThreadA(test02);
        ThreadB tb = new ThreadB(test03);

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

        ta.start();
        tb.start();
    }
結果可見,是異步執行的,沒有達到同步的做用。
name = A, begain
name = B, begain
name = A, end
name = B, end
改進:只需對類鎖進行調用,代碼以下:
class ThreadA extends Thread {
    
    private Test02 test02;
    
    public ThreadA(Test02 tk) {
        test02 = tk;
    }
    
    // 調用類鎖
    public void run() {
        test02.classLockA();
    }
}

class ThreadB extends Thread {
    
    private Test02 test02;
    
    public ThreadB(Test02 tk) {
        test02 = tk;
    }
    
    // 調用類鎖
    public void run() {
        test02.classLockA();
    }
}
main方法:一樣是建立了多個對象
public static void main(String[] args){

        Test02 test02 = new Test02();
        Test02 test03 = new Test02();
        ThreadA ta = new ThreadA(test02);
        ThreadB tb = new ThreadB(test03);

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

        ta.start();
        tb.start();
    }
結果達到了同步的效果!
name = A, begain
name = A, end
name = B, begain
name = B, end

總結:異步

  1. 若是多線程同時訪問同一類的 類鎖(synchronized 修飾的靜態方法)以及對象鎖(synchronized 修飾的非靜態方法)這兩個方法執行是異步的,緣由:類鎖和對象鎖是2中不一樣的鎖。
  2. 類鎖對該類的全部對象都能起做用,而對象鎖不能。
  3. synchronized鎖的是對象。
相關文章
相關標籤/搜索