1. 程式人生 > >類鎖與對象鎖,重入鎖

類鎖與對象鎖,重入鎖

內置鎖 內置 style adf ktr blog 必須 .net wid

看的該文章的總結:

https://juejin.im/post/5adf14dcf265da0b7b358d58

synchronized一般我們用來修飾三種東西:

  • 修飾普通方法
  • 修飾代碼塊
  • 修飾靜態方法

1.4.1修飾普通方法:

用的鎖是Java3y對象(內置鎖)

public class Java3y {


    // 修飾普通方法,此時用的鎖是Java3y對象(內置鎖)
    public synchronized void test() {
        // 關註公眾號Java3y
        // doSomething
    }
}

1.4.2修飾代碼塊:

用的鎖是Java3y對象(

內置鎖)--->this

public class Java3y {
    
    public  void test() {
        
        // 修飾代碼塊,此時用的鎖是Java3y對象(內置鎖)--->this
        synchronized (this){
            // 關註公眾號Java3y
            // doSomething
        }
    }
}

當然了,我們使用synchronized修飾代碼塊時未必使用this,還可以使用其他的對象(隨便一個對象都有一個內置鎖)

所以,我們可以這樣幹:

public
class Java3y { // 使用object作為鎖(任何對象都有對應的鎖標記,object也不例外) private Object object = new Object(); public void test() { // 修飾代碼塊,此時用的鎖是自己創建的鎖Object synchronized (object){ // 關註公眾號Java3y // doSomething } } }

上面那種方式(隨便使用一個對象作為鎖)在書上稱之為-->客戶端鎖,這是不建議使用的

書上想要實現的功能是:給ArrayList添加一個putIfAbsent(),這需要是線程安全的。

假定直接添加synchronized是不可行的

技術分享圖片

使用客戶端鎖,會將當前的實現與原本的list耦合了:

技術分享圖片

書上給出的辦法是使用組合的方式(也就是裝飾器模式)

技術分享圖片

1.4.3修飾靜態方法

獲取到的是類鎖(類的字節碼文件對象):Java3y.class

public class Java3y {

    // 修飾靜態方法代碼塊,靜態方法屬於類方法,它屬於這個類,獲取到的鎖是屬於類的鎖(類的字節碼文件對象)-->Java3y.class
    public static synchronized void test() {

        // 關註公眾號Java3y
        // doSomething
    }
}

1.4.4類鎖與對象鎖

synchronized修飾靜態方法獲取的是類鎖(類的字節碼文件對象),synchronized修飾普通方法或代碼塊獲取的是對象鎖。

  • 它倆是不沖突的,也就是說:獲取了類鎖的線程和獲取了對象鎖的線程是不沖突的!
public class SynchoronizedDemo {

    //synchronized修飾非靜態方法
    public synchronized void function() throws InterruptedException {
        for (int i = 0; i <3; i++) {
            Thread.sleep(1000);
            System.out.println("function running...");
        }
    }
    //synchronized修飾靜態方法
    public static synchronized void staticFunction()
            throws InterruptedException {
        for (int i = 0; i < 3; i++) {
            Thread.sleep(1000);
            System.out.println("Static function running...");
        }
    }

    public static void main(String[] args) {
        final SynchoronizedDemo demo = new SynchoronizedDemo();

        // 創建線程執行靜態方法
        Thread t1 = new Thread(() -> {
            try {
                staticFunction();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 創建線程執行實例方法
        Thread t2 = new Thread(() -> {
            try {
                demo.function();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        // 啟動
        t1.start();
        t2.start();
    }
}

結果證明:類鎖和對象鎖是不會沖突的!

技術分享圖片




 

1.5重入鎖

我們來看下面的代碼:

public class Widget {

    // 鎖住了
    public synchronized void doSomething() {
        ...
    }
}

public class LoggingWidget extends Widget {

    // 鎖住了
    public synchronized void doSomething() {
        System.out.println(toString() + ": calling doSomething");
        super.doSomething();
    }
}
  1. 當線程A進入到LoggingWidget的doSomething()方法時,此時拿到了LoggingWidget實例對象的鎖
  2. 隨後在方法上又調用了父類Widget的doSomething()方法,它又是被synchronized修飾
  3. 那現在我們LoggingWidget實例對象的鎖還沒有釋放,進入父類Widget的doSomething()方法還需要一把鎖嗎?

不需要的!

因為鎖的持有者是“線程”,而不是“調用”

線程A已經是有了LoggingWidget實例對象的鎖了,當再需要的時候可以繼續**“開鎖”**進去的

這就是內置鎖的可重入性。

1.6釋放鎖的時機

  1. 當方法(代碼塊)執行完畢後會自動釋放鎖,不需要做任何的操作。
  2. 當一個線程執行的代碼出現異常時,其所持有的鎖會自動釋放。
  • 不會由於異常導致出現死鎖現象~。

二、Lock顯式鎖

2.1Lock顯式鎖簡單介紹

Lock顯式鎖是JDK1.5之後才有的,之前我們都是使用Synchronized鎖來使線程安全的~

Lock顯式鎖是一個接口,我們來看看:

技術分享圖片

隨便翻譯一下他的頂部註釋,看看是幹嘛用的:

技術分享圖片

可以簡單概括一下:

  • Lock方式來獲取鎖支持中斷、超時不獲取、是非阻塞的
  • 提高了語義化,哪裏加鎖,哪裏解鎖都得寫出來
  • Lock顯式鎖可以給我們帶來很好的靈活性,但同時我們必須手動釋放鎖
  • 支持Condition條件對象
  • 允許多個讀線程同時訪問共享資源

2.2synchronized鎖和Lock鎖使用哪個

前面說了,Lock顯式鎖給我們的程序帶來了很多的靈活性,很多特性都是Synchronized鎖沒有的。那Synchronized鎖有沒有存在的必要??

必須是有的!!Lock鎖在剛出來的時候很多性能方面都比Synchronized鎖要好,但是從JDK1.6開始Synchronized鎖就做了各種的優化(畢竟親兒子,牛逼)

  • 優化操作:適應自旋鎖,鎖消除,鎖粗化,輕量級鎖,偏向鎖。
  • 詳情可參考:blog.csdn.net/chenssy/art…

所以,到現在Lock鎖和Synchronized鎖的性能其實差別不是很大!而Synchronized鎖用起來又特別簡單。Lock鎖還得顧忌到它的特性,要手動釋放鎖才行(如果忘了釋放,這就是一個隱患)

所以說,我們絕大部分時候還是會使用Synchronized鎖,用到了Lock鎖提及的特性,帶來的靈活性才會考慮使用Lock顯式鎖~

2.3公平鎖

公平鎖理解起來非常簡單:

  • 線程將按照它們發出請求的順序來獲取鎖

非公平鎖就是:

  • 線程發出請求的時可以**“插隊”**獲取鎖

Lock和synchronize都是默認使用非公平鎖的。如果不是必要的情況下,不要使用公平鎖

  • 公平鎖會來帶一些性能的消耗的

四、最後

本文講了synchronized內置鎖和簡單描述了一下Lock顯式鎖,總得來說:

  • synchronized好用,簡單,性能不差
  • 沒有使用到Lock顯式鎖的特性就不要使用Lock鎖了。

Lock鎖這裏只是介紹了一些些,明天會詳解它的相關子類和需要註意的地方,敬請期待~

之前在學習操作系統的時候根據《計算機操作系統-湯小丹》這本書也做了一點點筆記,都是比較淺顯的知識點。或許對大家有幫助

類鎖與對象鎖,重入鎖