深入理解Java鎖(Synchronized)

想要解決多線程問題,首先我們需要掌握Java鎖的原理。

我們常用鎖Synchronized的方式

一:方法鎖

1:方法鎖鎖的是該對象。例如:

 

public static void main(String[] arr) {
    final Test1 test1 = new Test1();
    for (int i = 0; i < 15; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                test1.send();
            }
        }).start();
    }
}
public class Test1 {
    int data;
    public synchronized void send() {
        System.out.println();
        System.out.println("方法入口******= "+
                Thread.currentThread().getName());
        for (int i = 0; i < 20000; i++) {
            data++;
        }
        System.out.println(data);
        System.out.println("方法出口&&&&&&= "+
                Thread.currentThread().getName());
        data = 0;
    }
}

 

輸出結果爲:

 

說明線程處於排隊狀態,該方法只允許一個線程執行,執行完之後釋放鎖,線程重新獲取鎖執行。

2:去掉send方法上的鎖 synchronized關鍵字

 

public class Test1 {
    int data;
    public  void send() {
        for (int i = 0; i < 20000; i++) {
            data++;
        }
        System.out.println(data);
        data = 0;
    }
}

 

public class Test1 {
    int data;
    public  void send() {
        System.out.println();
        System.out.println("方法入口******= "+
                Thread.currentThread().getName());
        for (int i = 0; i < 20000; i++) {
            data++;
        }
        System.out.println(data);
        System.out.println("方法出口&&&&&&= "+
                Thread.currentThread().getName());
        data = 0;
    }
}

 

輸出結果爲

 

由此可見,去掉鎖,則其中一個線程在執行該方法時,其他線程依然可以執行。

3:當多個實例調用send方法時

 

public static void main(String[] arr) {
    for (int i = 0; i < 15; i++) {
        final Test1 test1 = new Test1();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test1.send();
            }
        }).start();
    }
}

 

public class Test1 {
    int data;
    public  void send() {
        System.out.println();
        System.out.println("方法入口******= "+
                Thread.currentThread().getName());
        for (int i = 0; i < 20000; i++) {
            data++;
        }
        System.out.println(data);
        System.out.println("方法出口&&&&&&= "+
                Thread.currentThread().getName());
        data = 0;
    }
}

輸出結果爲:

所以由此說明,方法鎖,鎖的是當前實例。當有多個實例,鎖不受影響。

二:代碼塊鎖

該方式的鎖,比較靈活。既可以鎖當前實體,也可以鎖定當前類。鎖定當前實體,則跟方法鎖一樣,這裏不再說明。重點說下鎖定類。

1:鎖定類。當鎖定類的時候,不論是單一實體還是多實體的多線程請求該段上鎖的代碼塊,都將搶鎖,其他線程處於等待。

 

public static void main(String[] arr) {
    for (int i = 0; i < 10; i++) {
        final Test1 test1 = new Test1();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test1.send();
            }
        }).start();
    }
}
public class Test1 {
    int data;
    public  void send() {
       synchronized (Test1.class){
           System.out.println();
           System.out.println("方法入口******= "+
                   Thread.currentThread().getName());
           for (int i = 0; i < 20000; i++) {
               data++;
           }
           System.out.println(data);
           System.out.println("方法出口&&&&&&= "+
                   Thread.currentThread().getName());
           data = 0;
       }
    }
}

輸出結果爲:

由此說明,鎖定類,即使是多實體的多線程,依然是有效果的。

 

總結:

1:當一個類中有多個方法鎖時,具有方法鎖的方法則彼此間可以互相調用,因爲此時都屬於同一線程,並且又是同一把鎖,所以不會受影響。

2:當一個線程獲取到該對象的方法鎖時,其他線程無法獲取該對象的方法鎖及該對應對應的代碼塊鎖。

3:代碼塊鎖中的鎖,可以靈活變換,可以鎖定其他實體,也可以鎖定該片段爲類的鎖,使該類的多實體多線程搶用同一鎖,其他線程處於等待狀態。

4:代碼塊鎖在一個線程搶佔資源後,其他線程處於等待狀態,而並不是該線程執行完之後其他線程纔可執行,當一個線程for循環執行一個代碼塊鎖。在執行一次後會釋放鎖,然後共同搶鎖,有可能出現for循環只執行了一次,鎖的資源被其他線程搶走了。所以在處理業務邏輯要特別注意。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章