成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

被問到ReentrantLock,你真的能答好嗎?

開發
ReentrantLock作為最常用的獨占鎖,其內部就是包含了AQS的子類實現了線程搶鎖和釋放鎖的邏輯。那么ReentrantLock是怎么實現的呢?

一、先了解一下

我們知道實現一把鎖要有如下幾個邏輯:

  • 鎖的標識
  • 線程搶鎖的邏輯
  • 線程掛起的邏輯
  • 線程存儲邏輯
  • 線程釋放鎖的邏輯
  • 線程喚醒的邏輯

我們在講解AQS的時候說過AQS基本負責了實現鎖的全部邏輯,唯獨線程搶鎖和線程釋放鎖的邏輯是交給子類來實現了,而ReentrantLock作為最常用的獨占鎖,其內部就是包含了AQS的子類實現了線程搶鎖和釋放鎖的邏輯。

我們在使用ReentrantLock的時候一般只會使用如下方法:

ReentrantLock lock=new ReentrantLock();
 lock.lock();
 lock.unlock();
 lock.tryLock();
 Condition condition=lock.newCondition();
 condition.await();
 condition.signal();
 condition.signalAll();

二、技術架構

如果我們自己來實現一個鎖,那么如何設計呢?

根據AQS的邏輯,我們寫一個子類sync,這個類一定會調用父類的acquire方法進行上鎖,同時重寫tryAcquire方法實現自己搶鎖邏輯,也一定會調用release方法進行解鎖,同時重寫tryRelease方法實現釋放鎖邏輯。

那么ReentrantLock是怎么實現的呢?

ReentrantLock的實現的類架構如下,ReentrantLock對外提供作為一把鎖應該具備的api,比如lock加鎖,unlock解鎖等等,而它內部真正的實現是通過靜態內部類sync實現,sync是AQS的子類,是真正的鎖,因為這把鎖需要支持公平和非公平的特性,所以sync又有兩個子類FairSync和NonfairSync分別實現公平鎖和非公平鎖。

因為是否公平說的是搶鎖的時候是否公平,那兩個子類就要在上鎖方法acquire的調用和搶鎖方法tryAcquire的重寫上做文章。

公平鎖做了什么文章?

static final class FairSync extends Sync {
  
        final void lock() {
            acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

公平鎖比較簡單,直接調用了父級類AQS的acquire方法,因為AQS的鎖默認就是公平的排隊策略。

重寫tryAcquire方法的邏輯為:

  • 判斷當前鎖是否被占用,即state是否為0
  • 如果當前鎖沒有被占用,然后會判斷等待隊列中是否有線程在阻塞等待,如果有,那就終止搶鎖,如果沒有,就通過cas搶鎖,搶到鎖返回true,沒有搶到鎖返回false。
  • 如果當前鎖已經被占用,然后判斷占用鎖的線程是不是自己,如果是,就會將state加1,表示重入,返回true。如果不是自己那就是代表沒有搶到鎖,返回false。

公平就公平在老老實實排隊。

非公平鎖做了什么文章?

static final class NonfairSync extends Sync {
      
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    
    //nonfairTryAcquire代碼在父類sync里面
     final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

非公平鎖也很簡單,沒有直接調用了父級類AQS的acquire方法,而是先通過cas搶鎖,它不管等待隊列中有沒有其他線程在排隊,直接搶鎖,這就體現了不公平。

它重寫tryAcquire方法的邏輯為:

  • 判斷當前鎖是否被占用,即state是否為0
  • 如果當前鎖沒有被占用,就直接通過cas搶鎖(不管等待隊列中有沒有線程在排隊),搶到鎖返回true,沒有搶到鎖返回false。
  • 如果當前鎖已經被占用,然后判斷占用鎖的線程是不是自己,如果是,就會將state加1,表示重入,返回true。如果不是自己那就是代表沒有搶到鎖,返回false。

公平鎖和非公平分別重寫了tryAcquire方法,來滿足公平和非公平的特性。那么tryAcquire方法也是需要子類重寫的,因為它和是否公平無關,因此tryAcquire方法被抽象到sync類中重寫。

sync類中
protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

釋放鎖的邏輯如下:

  • 獲取state的值,然后減1
  • 如果state為0,代表鎖已經釋放,清空aqs中的持有鎖的線程字段的值
  • 如果state不為0,說明當前線程重入了,還需要再次釋放鎖
  • 將state寫回

釋放鎖往往和搶鎖邏輯是對應的,每個子類搶鎖邏輯不同的話,釋放鎖的邏輯也會對應不同。

三、具體實現

接下來我們通過ReentrantLock的使用看下它的源碼實現:

class X {
            private final ReentrantLock lock = new ReentrantLock();
            Condition condition1=lock.newCondition();
            Condition condition2=lock.newCondition();
            public void m() {
                lock.lock();
                try {

                    if(條件1){
                        condition1.await();
                    }
                    if(條件2){
                        condition2.await();
                    }
                } catch (InterruptedException e) {

                } finally {
                    condition1.signal();
                    condition2.signal();
                    lock.unlock();
                }
            }
        }

1.先看這個方法:lock.lock()

ReentrantLock類
public void lock() {
        sync.lock();
    }
NonfairSync 類中
  final void lock() {
    if (compareAndSetState(0, 1))
    setExclusiveOwnerThread(Thread.currentThread());
    else
      acquire(1);
  }
FairSync 類中
  final void lock() {
      acquire(1);
  }

公平鎖和非公平鎖中都實現了lock方法,公平鎖直接調用AQS的acquire,而非公平鎖先搶鎖,搶不到鎖再調用AQS的acquire方法進行上鎖

進入acquire方法后的邏輯我們就都知道了。

2.再看這個方法lock.unlock()

public void unlock() {
        sync.release(1);
}

unlock方法內直接調用了AQS的Release方法進行解鎖的邏輯,進入release方法后邏輯我們都已經知道了,這里不再往下跟。

3.最后看這個方法lock.tryLock()

這個方法中直接調用了sync中的nonfairTryAcquire方法,這個就是非公平鎖實現的TryAcquire方法,只是名字改了而已。

tryLock方法的意思是嘗試搶鎖,是給程序員調用的嘗試搶鎖方法,如果搶鎖成功返回true,搶鎖失敗返回false,當拿到搶鎖失敗的信號后,程序員可以做一些自己的策略。

如果沒有tryLock方法,而是直接調用lock方法,就會走到acquire方法中,Acquire方法中也會調用TryAcquire方法,只不過在這里如果獲取不到鎖就會入隊阻塞了,就沒有程序員參與的可能了。

4.lock.newCondition();

在AQS那篇我們說過Condition是AQS中的條件隊列,可以按條件將一批線程由不可喚醒變為可喚醒。

ReentrantLock類
 public Condition newCondition() {
        return sync.newCondition();
 }
sync靜態內部類
final ConditionObject newCondition() {
            return new ConditionObject();
}

sync提供了創建Condition對象的方法,意味著ReentrantLock也擁有Condition的能力。

四、ReentrantLock和synchronized對比

我們下面說的ReentrantLock其實就是說AQS,因為它的同步實現主要在AQS里面。

1.實現方面

  • ReentrantLock是jdk級別實現的,其源碼在jdk源碼中可以查看,沒有脫離java。
  • synchronized是jvm級別實現的,synchronized只是java端的一個關鍵字,具體邏輯實現都在jvm中。

2.性能方面

優化前的synchronized性能很差,主要表現在兩個方面:

  • 因為大多數情況下對于資源的爭奪并沒有那么激烈,甚至于某個時刻可能只有一個線程在工作,在這種沒有競爭或者競爭壓力很小的情況下,如果每個線程都要進行用戶態到內核態的切換其實是很耗時的。
  • jdk1.6對synchronized底層實現做了優化,優化后,在單線程以及并發不是很高的情況下通過無鎖偏向和自旋鎖的方式避免用戶態到內核態的切換,因此性能提高了,優化后的synchronized和ReentrantLock性能差不多了。

ReentrantLock是在jdk實現的,它申請互斥量就是對鎖標識state的爭奪,它是通過cas方式實現。在java端實現。

對于爭奪不到資源的線程依然要阻塞掛起,但凡阻塞掛起都要依賴于操作系統底層,這一步的用戶態到內核態的切換是避免不了的。

因此在單線程進入代碼塊的時候,效率是很高的,因此我們說ReentrantLock性能高于原始的synchronized:

  • 申請互斥量:synchronized的鎖其實就是爭奪Monitor鎖的擁有權,這個爭奪過程是通過操作系統底層的互斥原語Mutex實現的,這個過程會有用戶態到內核態的切換。
  • 線程阻塞掛起:沒能搶到到Monitor鎖擁有權的線程要阻塞掛起,阻塞掛起這個動作也是依靠操作系統實現的,這個過程也需要用戶態到內核態的切換。

3.特性方面

兩個都是常用的典型的獨占鎖。

  • ReentrantLock可重入,可中斷,支持公平和非公平鎖,可嘗試獲取鎖,可以支持分組將線程由不可喚醒變為可喚醒。
  • synchronized可重入,不可中斷,非公平鎖,不可嘗試獲取鎖,只支持一個或者全部線程由不可喚醒到可喚醒。

4.使用方面

synchronized不需要手動釋放鎖,ReentrantLock需要手動釋放鎖,需要考慮異常對釋放鎖的影響避免異常導致線程一直持有鎖。

以下是兩個鎖的使用方式:

class X {
            private final ReentrantLock lock = new ReentrantLock();
            Condition condition1=lock.newCondition();
            Condition condition2=lock.newCondition();
            public void m() {
                lock.lock();
                try {

                    if(1==2){
                        condition1.await();
                    }
                    if(1==3){
                        condition2.await();
                    }
                } catch (InterruptedException e) {

                } finally {
                    condition1.signal();
                    condition2.signal();
                    lock.unlock();
                }
            }
        }
class X {
            private final testtest sync=new testtest();;
            public void m() throws InterruptedException {
                synchronized(sync){
                    if(1==2){
                        sync.wait();
                    }
                    sync.notify();
                    sync.notifyAll();
                }
            }
        }

對比代碼及特性說明:

  • 兩個鎖都是依賴一個對象:lock和sync
  • condition和wait方法具有同樣的效果,進入condition和wait的線程將陷入等待(不可喚醒狀態),只有被分別調用signal和notify方法線程才會重新變為可喚醒狀態,請注意是可喚醒,而不是被喚醒。
  • 可喚醒是說具備了競爭資源的資格,資源空閑后,synchronized中會在可喚醒狀態的線程中隨機挑選一個線程去拿鎖,而ReentrantLock中不可喚醒的線程變為可喚醒狀態,其實就是將條件隊列中的線程搬到等待隊列中排隊,只有隊頭的才會去嘗試拿鎖。
  • ReentrantLock分批將線程由不可喚醒變為可喚醒也在這段代碼中體現了,代碼中按照不同的條件將線程放入不同的condition,每個condition就是一個組,釋放的時候也可以按照不同的條件進行釋放。而synchronized中進入wait的線程不能分組,釋放也只能隨機釋放一個或者全部釋放。
責任編輯:趙寧寧 來源: 碼農本農
相關推薦

2023-10-11 08:22:33

線程AQScondition

2023-10-29 17:08:38

AQS線程

2017-09-07 16:32:05

華為

2020-12-01 07:16:05

重學設計模式

2016-08-16 13:30:49

LinuxCachetmpfs

2018-08-02 10:33:42

2009-04-13 09:35:50

SaaS云計算缺陷

2022-05-20 15:27:41

React工具Vue

2013-06-26 10:14:40

2021-11-16 23:05:53

人工智能工具教育

2010-07-27 16:07:25

2018-07-04 14:28:02

2019-07-16 10:10:46

JavaScript數據類型

2012-05-10 09:37:25

CIO云計算云服務

2012-05-11 09:45:07

海量數據

2017-11-13 14:06:56

2015-07-02 10:12:24

程序員

2019-12-10 10:59:11

分層架構項目

2015-03-06 09:47:53

小米變化

2017-12-18 17:21:56

AndroidJava內存泄漏
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 仙人掌旅馆在线观看 | 欧美成人影院在线 | 日韩网站在线 | 在线精品亚洲欧美日韩国产 | 成人免费淫片aa视频免费 | 午夜精品久久久久久久久久久久久 | 日韩av高清 | 久久久91精品国产一区二区三区 | av特级毛片 | 男人天堂av网站 | 91黄色免费看 | 91精品国产综合久久香蕉麻豆 | 久久久久精| 欧美精品二区三区 | 91久久精品| 国产区一区 | 国产在线精品一区二区 | 亚洲黄色一区二区三区 | 亚洲高清一区二区三区 | 久久久久久久综合色一本 | 黑人精品| 亚洲国产午夜 | 久久精品欧美一区二区三区不卡 | 日韩欧美在线一区二区 | 日本精品久久久一区二区三区 | 91欧美精品成人综合在线观看 | 91成人午夜性a一级毛片 | 黄色一级大片在线免费看产 | 亚洲一区二区精品视频在线观看 | 伊人伊人 | 日本精品在线一区 | 日韩欧美在线观看视频 | 91免费版在线 | 久久精品免费观看 | 黑人精品欧美一区二区蜜桃 | 国产高清免费 | 999观看免费高清www | 成人免费在线网 | 黄色在线免费看 | 很很干很很日 | 久久噜噜噜精品国产亚洲综合 |