高并發(fā)設(shè)計之細(xì)粒度鎖 : 5種細(xì)粒度鎖的設(shè)計技巧圖解(高并發(fā)篇)

image.png

在現(xiàn)代并發(fā)編程的迷宮中浮禾,鎖是保護(hù)數(shù)據(jù)完整性的守護(hù)者。從基礎(chǔ)的互斥鎖(Mutex)確保單一線程訪問蝉稳,到讀寫鎖(Read-Write Locks)平衡讀多寫少的場景描馅,再到樂觀鎖(Optimistic Locking)減少鎖的競爭,以及悲觀鎖(Pessimistic Locking)應(yīng)對高沖突環(huán)境更振,每種鎖都有其獨特的用武之地。而細(xì)粒度鎖(Fine-Grained Lock)則以其在更小的數(shù)據(jù)粒度上操作,進(jìn)一步優(yōu)化了并發(fā)控制土陪。本文將帶您一探這些鎖的神秘面紗,了解它們?nèi)绾螀f(xié)同工作以提升系統(tǒng)性能肴熏,同時確保數(shù)據(jù)安全鬼雀。無論您是資深開發(fā)者還是并發(fā)領(lǐng)域的新手,這些鎖的策略和實踐都將是您構(gòu)建高效蛙吏、穩(wěn)定系統(tǒng)的重要工具源哩。

肖哥彈架構(gòu) 跟大家“彈彈” 高并發(fā)鎖, 關(guān)公回 'mvcc' 獲得手寫數(shù)據(jù)庫事務(wù)代碼

歡迎 點贊鸦做,關(guān)注励烦,評論。

歷史熱點文章

1坛掠、細(xì)粒度鎖實現(xiàn)流程策略

image.png

細(xì)粒度鎖內(nèi)部實現(xiàn)思路

  • 初始化鎖:在應(yīng)用程序啟動或資源創(chuàng)建時,初始化鎖對象治筒。
  • 請求鎖:當(dāng)線程需要訪問受保護(hù)的資源時屉栓,請求鎖。
  • 鎖是否可用:檢查鎖是否已經(jīng)被其他線程占用耸袜。
  • 獲取鎖:如果鎖可用系瓢,當(dāng)前線程獲取鎖。
  • 等待鎖釋放:如果鎖不可用句灌,線程進(jìn)入等待狀態(tài)夷陋,直到鎖被釋放。
  • 執(zhí)行受保護(hù)的操作:線程在獲取鎖后執(zhí)行需要保護(hù)的操作胰锌。
  • 操作是否完成:檢查受保護(hù)的操作是否已經(jīng)完成骗绕。
  • 釋放鎖:操作完成后,線程釋放鎖资昧,允許其他線程獲取鎖酬土。
  • 通知等待線程:釋放鎖后,如果有線程在等待格带,通知它們鎖已可用撤缴。

細(xì)粒度鎖的本質(zhì)思路是將鎖的粒度細(xì)化到最小必要的范圍刹枉,以減少鎖競爭和提高并發(fā)性能。以下是細(xì)粒度鎖的幾個核心原則和思路:

  1. 最小化鎖范圍
    • 細(xì)粒度鎖將鎖的作用范圍限制在最小的數(shù)據(jù)單元或操作上屈呕,而不是對整個數(shù)據(jù)結(jié)構(gòu)或資源加鎖微宝。
  2. 減少鎖持有時間
    • 通過快速完成操作并盡早釋放鎖,減少每個線程持有鎖的時間虎眨,從而減少其他線程的等待時間蟋软。
  3. 鎖分離
    • 將不同的操作或數(shù)據(jù)訪問分離到不同的鎖上,使得多個操作可以并行執(zhí)行嗽桩,而不是所有操作都依賴于同一個鎖岳守。
  4. 鎖分段
    • 將數(shù)據(jù)結(jié)構(gòu)分割成多個段,每個段有自己的鎖碌冶,這樣可以同時對不同段進(jìn)行操作而不會相互阻塞湿痢。
  5. 無鎖編程
    • 利用原子操作和數(shù)據(jù)結(jié)構(gòu)來避免使用鎖,通過CAS(Compare-And-Swap)等機制來保證數(shù)據(jù)的一致性扑庞。
  6. 讀寫鎖
    • 允許多個讀操作同時進(jìn)行蒙袍,但寫操作需要獨占鎖,以此來提高讀操作的并發(fā)性嫩挤。
  7. 鎖粗化
    • 在某些情況下害幅,如果一個線程需要連續(xù)訪問多個資源,可以考慮將鎖的范圍擴(kuò)大岂昭,以減少頻繁的鎖獲取和釋放以现。
  8. 鎖升級和降級
    • 根據(jù)實際情況動態(tài)調(diào)整鎖的粒度,例如從讀鎖升級到寫鎖约啊,或者在不同級別的鎖之間進(jìn)行切換邑遏。
  9. 避免死鎖
    • 設(shè)計鎖策略時,確保鎖的獲取和釋放順序一致恰矩,避免出現(xiàn)死鎖记盒。
  10. 性能監(jiān)控
    • 監(jiān)控鎖的性能,包括鎖的競爭率外傅、等待時間和吞吐量纪吮,以便根據(jù)實際情況調(diào)整鎖策略。

2萎胰、細(xì)顆粒鎖使用通用流程

image.png

細(xì)粒度鎖使用流程說明

  • 資源訪問請求:線程請求訪問受保護(hù)的資源碾盟。
  • 檢查鎖狀態(tài):線程檢查細(xì)粒度鎖的狀態(tài),確定是否可以獲取鎖技竟。
  • 鎖定:如果鎖可用冰肴,線程獲取鎖并執(zhí)行資源操作。
  • 執(zhí)行資源操作:線程對資源進(jìn)行操作,如讀取熙尉、修改等联逻。
  • 等待鎖可用:如果鎖不可用,線程進(jìn)入等待狀態(tài)检痰,直到鎖被釋放包归。
  • 操作完成:線程完成資源操作。
  • 釋放鎖:線程釋放鎖攀细,允許其他等待的線程獲取鎖。
  • 資源訪問者等待隊列:等待鎖的線程排隊等待爱态。

3谭贪、HikariCP在連接池中細(xì)顆粒鎖落地思路

image.png

細(xì)粒度鎖在連接池落地設(shè)計說明

  • 連接池管理器:負(fù)責(zé)整個連接池的管理和調(diào)度。
  • 連接對象池:存儲所有可用的數(shù)據(jù)庫連接對象锦担。
  • 細(xì)粒度鎖:為每個連接或連接組提供獨立的鎖俭识,以減少鎖競爭。
  • 連接狀態(tài):記錄每個連接的當(dāng)前狀態(tài)(如空閑洞渔、使用中套媚、失效等)。
  • 連接屬性:存儲每個連接的配置和屬性磁椒。
  • 連接操作:管理連接的創(chuàng)建堤瘤、使用和銷毀等操作。
  • 應(yīng)用程序線程:請求和使用數(shù)據(jù)庫連接的應(yīng)用程序線程浆熔。
  • 連接請求隊列:管理連接請求本辐,確保按順序分配連接。
  • 應(yīng)用程序操作:應(yīng)用程序?qū)?shù)據(jù)庫連接進(jìn)行的操作医增。
  • 連接釋放隊列:管理連接釋放請求慎皱,確保連接被正確回收。
  • 監(jiān)控線程:定期檢查連接狀態(tài)和性能指標(biāo)叶骨,確保連接池的健康茫多。

4、細(xì)顆粒鎖使用案例

4.1. 讀寫鎖(ReadWriteLock)

ReadWriteLock 的本質(zhì)思路是允許多個讀操作同時進(jìn)行忽刽,但寫操作是獨占的天揖,以此來提高并發(fā)性能。這種設(shè)計模式通常用于讀多寫少的場景跪帝。

場景描述:在高并發(fā)的Web應(yīng)用中宝剖,緩存系統(tǒng)需要頻繁地讀取數(shù)據(jù),而寫入操作相對較少歉甚。使用讀寫鎖可以提高讀取操作的并發(fā)性万细,減少鎖的競爭。

應(yīng)用代碼

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CacheService {
    private final Map<String, Object> cache = new ConcurrentHashMap<>();
    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    public Object getValue(String key) {
        lock.readLock().lock();
        try {
            return cache.get(key);
        } finally {
            lock.readLock().unlock();
        }
    }

    public void setValue(String key, Object value) {
        lock.writeLock().lock();
        try {
            cache.put(key, value);
        } finally {
            lock.writeLock().unlock();
        }
    }
}
ReadWriteLock 類設(shè)計
image.png

UML類圖中:

  • ReadWriteLock 是一個接口,定義了獲取讀鎖和寫鎖的方法赖钞。
  • ReentrantReadWriteLockReadWriteLock 的具體實現(xiàn)腰素,它內(nèi)部維護(hù)了讀鎖和寫鎖的狀態(tài)。
  • ReentrantReadLockReentrantWriteLock 是內(nèi)部類雪营,分別實現(xiàn)了讀鎖和寫鎖的行為弓千。
  • Lock 是一個接口,定義了鎖的基本操作献起,如 lock()洋访、unlock()tryLock() 等谴餐。
  • ReentrantReadWriteLock 包含了 ReentrantReadLockReentrantWriteLock姻政,用于管理讀鎖和寫鎖。
ReadWriteLock設(shè)計思路
image.png
核心思路
  1. 讀鎖(Shared Lock)
    • 當(dāng)一個線程獲得讀鎖時岂嗓,它可以讀取數(shù)據(jù)汁展。
    • 多個線程可以同時持有讀鎖,這意味著多個線程可以同時讀取數(shù)據(jù)厌殉,而不會互相阻塞食绿。
  2. 寫鎖(Exclusive Lock)
    • 當(dāng)一個線程獲得寫鎖時,它可以修改數(shù)據(jù)公罕。
    • 寫鎖是獨占的器紧,這意味著在任何時候只能有一個線程持有寫鎖。
    • 當(dāng)寫鎖被持有時楼眷,所有其他讀和寫請求都必須等待直到寫鎖被釋放品洛。
  3. 鎖升級和降級
    • 鎖升級:從讀鎖升級到寫鎖。這通常需要先釋放讀鎖摩桶,然后獲取寫鎖桥状。升級過程中可能會引入競態(tài)條件,需要謹(jǐn)慎處理硝清。
    • 鎖降級:從寫鎖降級到讀鎖辅斟。這通常比較安全,因為寫鎖持有者在釋放寫鎖之前已經(jīng)確保了數(shù)據(jù)的一致性芦拿。
實現(xiàn)機制
  1. 鎖狀態(tài)管理
    • 鎖狀態(tài)通常由一個計數(shù)器來管理士飒,用于跟蹤當(dāng)前持有鎖的讀線程數(shù)量和是否有寫線程持有鎖。
  2. 讀鎖的獲取
    • 如果沒有線程持有寫鎖蔗崎,讀線程可以增加讀鎖計數(shù)器并立即獲取讀鎖酵幕。
    • 如果有線程持有寫鎖,讀線程必須等待寫鎖釋放缓苛。
  3. 寫鎖的獲取
    • 寫線程必須等待所有讀鎖和寫鎖都被釋放后才能獲取寫鎖芳撒。
    • 這通常涉及到阻塞等待,直到讀鎖計數(shù)器為零且沒有其他寫線程競爭。
  4. 鎖的釋放
    • 讀鎖的釋放涉及到減少讀鎖計數(shù)器笔刹。
    • 寫鎖的釋放允許其他等待的讀或?qū)懢€程嘗試獲取鎖芥备。
  5. 鎖的公平性
    • 鎖可以是公平的或非公平的。
    • 公平鎖確保等待時間最長的線程首先獲得鎖舌菜,而非公平鎖則允許線程搶占鎖萌壳。
  6. 鎖的重入
    • 讀鎖和寫鎖都支持重入,這意味著同一個線程可以多次獲取同一把鎖日月。
  7. 鎖的中斷
    • 鎖的獲取操作可以響應(yīng)中斷袱瓮,這意味著線程在等待鎖的過程中可以被中斷。

4.2. 分段鎖(Segmented Locks)

分段鎖(Segmented Locks)是一種用于提高并發(fā)性能的鎖策略爱咬,它通過將數(shù)據(jù)結(jié)構(gòu)分割成多個段(或分區(qū))尺借,并為每個段提供獨立的鎖,從而允許多個線程同時對不同段進(jìn)行操作台颠。這種策略特別適用于那些需要頻繁訪問的大型數(shù)據(jù)結(jié)構(gòu)褐望,如哈希表勒庄、數(shù)組或其他集合類型串前。

場景描述:在分布式數(shù)據(jù)庫系統(tǒng)中,數(shù)據(jù)被分割成多個片段(shards)实蔽,每個片段可以獨立地進(jìn)行操作荡碾。使用分段鎖可以確保對不同片段的操作不會相互干擾。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ShardedDatabase {
    private final Map<Integer, Lock> locks = new ConcurrentHashMap<>();
    private final Map<Integer, Map<String, Object>> shards = new ConcurrentHashMap<>();

    public ShardedDatabase(int numShards) {
        for (int i = 0; i < numShards; i++) {
            locks.put(i, new ReentrantLock());
            shards.put(i, new ConcurrentHashMap<>());
        }
    }

    public void updateShard(int shardId, String key, Object value) {
        locks.get(shardId).lock();
        try {
            shards.get(shardId).put(key, value);
        } finally {
            locks.get(shardId).unlock();
        }
    }

    public Object getShardValue(int shardId, String key) {
        locks.get(shardId).lock();
        try {
            return shards.get(shardId).get(key);
        } finally {
            locks.get(shardId).unlock();
        }
    }
}
Segmented Locks 類設(shè)計
image.png

圖形解釋:

  • SegmentedHashTable

    • 包含一個整型變量segmentCount局装,表示分段的數(shù)量坛吁。
    • 包含一個Segment數(shù)組_segments,每個元素代表一個數(shù)據(jù)段铐尚。
    • 提供getSegment方法拨脉,根據(jù)鍵的哈希值確定應(yīng)該訪問哪個段。
    • 提供getput方法宣增,用于客戶端訪問和修改哈希表玫膀。
  • Segment

    • 包含一個Lock對象lock,用于控制對該段的并發(fā)訪問爹脾。
    • 包含一個HashMap對象map帖旨,用于存儲該段的數(shù)據(jù)。
    • 提供getput方法灵妨,用于在該段內(nèi)進(jìn)行數(shù)據(jù)的讀取和寫入操作解阅。
  • 關(guān)系

    • SegmentedHashTable包含多個Segment對象,表示它是由多個段組成的泌霍。
Segmented Locks設(shè)計思路
image.png
核心思路
  1. 數(shù)據(jù)分段
    • 將數(shù)據(jù)結(jié)構(gòu)分割成多個邏輯段或分區(qū)货抄,每個段包含數(shù)據(jù)結(jié)構(gòu)的一部分。
    • 每個段可以獨立于其他段進(jìn)行操作,減少了鎖的競爭碉熄。
  2. 獨立鎖
    • 為每個數(shù)據(jù)段分配一個獨立的鎖桨武。
    • 當(dāng)一個線程需要操作某個數(shù)據(jù)段時,它只需獲取該段的鎖锈津,而不影響其他段的操作呀酸。
  3. 并發(fā)訪問
    • 允許多個線程同時訪問不同的數(shù)據(jù)段,因為每個段的鎖是獨立的琼梆。
    • 這種設(shè)計顯著提高了并發(fā)性能性誉,尤其是在讀多寫少的場景中。
  4. 鎖粒度
    • 分段鎖通過減小鎖的粒度來減少鎖的競爭茎杂。
    • 相比于全局鎖错览,分段鎖允許更細(xì)粒度的并發(fā)控制。
  5. 平衡性能與復(fù)雜性
    • 分段鎖增加了實現(xiàn)的復(fù)雜性煌往,因為需要管理多個鎖和數(shù)據(jù)段倾哺。
    • 但是,它通常能提供更好的性能刽脖,特別是在高并發(fā)環(huán)境下羞海。
  6. 避免熱點
    • 在全局鎖的情況下,數(shù)據(jù)結(jié)構(gòu)的某些部分可能會成為熱點曲管,導(dǎo)致大量的線程競爭同一資源却邓。
    • 分段鎖通過分散訪問壓力,減少了熱點問題院水。
  7. 動態(tài)調(diào)整
    • 根據(jù)實際的訪問模式和性能需求腊徙,可以動態(tài)地調(diào)整段的大小和數(shù)量。
    • 這允許系統(tǒng)在運行時優(yōu)化性能檬某。
實現(xiàn)注意事項
  • 鎖管理
    • 需要有效管理鎖的獲取和釋放撬腾,以避免死鎖和性能瓶頸。
  • 數(shù)據(jù)一致性
    • 在跨段操作時恢恼,需要確保數(shù)據(jù)的一致性和完整性民傻。
    • 可能需要額外的同步機制來處理涉及多個段的操作。
  • 負(fù)載均衡
    • 需要合理分配數(shù)據(jù)到各個段厅瞎,以避免某些段過載而其他段空閑饰潜,這可能導(dǎo)致性能瓶頸。
  • 擴(kuò)展性
    • 設(shè)計時需要考慮系統(tǒng)的擴(kuò)展性和簸,確保在增加更多的段或鎖時彭雾,系統(tǒng)的性能不會受到負(fù)面影響。

4.3. 無鎖數(shù)據(jù)結(jié)構(gòu)(Lock-Free Data Structures)

無鎖數(shù)據(jù)結(jié)構(gòu)(Lock-Free Data Structures)是一種并發(fā)編程技術(shù)锁保,旨在避免使用傳統(tǒng)的鎖機制來同步對共享數(shù)據(jù)的訪問薯酝。無鎖算法利用原子操作和內(nèi)存模型來保證數(shù)據(jù)的一致性和線程之間的協(xié)調(diào)半沽,從而減少鎖帶來的開銷和潛在的死鎖問題。
場景描述:在高并發(fā)系統(tǒng)中吴菠,如在線廣告點擊計數(shù)者填,使用無鎖數(shù)據(jù)結(jié)構(gòu)可以避免鎖的開銷,提高計數(shù)的效率做葵。

import java.util.concurrent.atomic.AtomicInteger;

public class ClickCounter {
    private final AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet();
    }

    public int getCount() {
        return count.get();
    }
}
AtomicInteger 類設(shè)計
image.png

類設(shè)計圖解釋

  • AtomicInteger:

    • 屬性:

      • int value: 存儲整數(shù)值的變量占哟,通常被標(biāo)記為 volatile 以確保內(nèi)存可見性和防止指令重排序。
    • 方法:

      • 構(gòu)造函數(shù) (AtomicInteger(int initialValue)): 初始化 AtomicInteger 的值酿矢。
      • get(): 返回當(dāng)前值榨乎。
      • set(int newValue): 設(shè)置當(dāng)前值。
      • getAndSet(int newValue): 獲取當(dāng)前值瘫筐,并設(shè)置為新值蜜暑。
      • getAndIncrement(): 獲取當(dāng)前值,并自增策肝。
      • getAndDecrement(): 獲取當(dāng)前值肛捍,并自減。
      • incrementAndGet(): 自增之众,并返回新值拙毫。
      • decrementAndGet(): 自減,并返回新值酝枢。
      • compareAndSet(int expect, int update): 如果當(dāng)前值等于預(yù)期值恬偷,則原子地更新為新值悍手。
      • weakCompareAndSet(int expect, int update): 與 compareAndSet 類似帘睦,但可能不具有相同的內(nèi)存語義。
      • getAndAdd(int delta): 獲取當(dāng)前值坦康,并添加指定的增量竣付。
      • addAndGet(int delta): 添加指定的增量,并返回新值滞欠。
  • AtomicReference:

    • 這是一個假設(shè)的內(nèi)部類或輔助類古胆,用于展示 AtomicInteger 可能使用的底層原子引用實現(xiàn)。

    • 屬性:

      • int value: 存儲整數(shù)值筛璧。
    • 方法:

      • 構(gòu)造函數(shù) (AtomicReference(int initialValue)): 初始化引用的值逸绎。
      • get(): 返回當(dāng)前引用的值。
      • compareAndSet(int expect, int update): 如果當(dāng)前引用的值等于預(yù)期值夭谤,則原子地更新為新值棺牧。
      • set(int newValue): 設(shè)置引用的值。
Lock-Free設(shè)計思路
image.png
核心思路
  1. 避免阻塞
    • 無鎖數(shù)據(jù)結(jié)構(gòu)通過非阻塞算法來避免線程因等待鎖而進(jìn)入阻塞狀態(tài)朗儒。
  2. 原子操作
    • 利用原子操作(如CAS - Compare-And-Swap)來確保數(shù)據(jù)項的更新是不可分割的颊乘。
  3. 避免死鎖
    • 由于不使用鎖参淹,無鎖數(shù)據(jù)結(jié)構(gòu)自然避免了死鎖的可能性。
  4. 順序保證
    • 通過內(nèi)存模型和原子操作來保證操作的順序性乏悄,確保數(shù)據(jù)的一致性浙值。
  5. 避免饑餓
    • 設(shè)計算法以確保所有線程最終都能完成其操作,避免某些線程被無限期地延遲檩小。
  6. 利用多核優(yōu)勢
    • 無鎖數(shù)據(jù)結(jié)構(gòu)可以更好地利用多核處理器的并行處理能力开呐。
實現(xiàn)機制
  1. 原子變量
    • 使用原子變量(如AtomicIntegerAtomicReference等)來存儲數(shù)據(jù)項规求,這些變量提供了原子操作负蚊。
  2. 比較并交換(CAS)
    • CAS操作是無鎖數(shù)據(jù)結(jié)構(gòu)的核心,它包括三個參數(shù):內(nèi)存位置(V)颓哮、預(yù)期原值(A)和新值(B)家妆。如果內(nèi)存位置的值與預(yù)期原值相等,則將該位置值更新為新值冕茅。
  3. 失敗重試
    • 當(dāng)CAS操作失敗時(通常是因為其他線程已經(jīng)更改了數(shù)據(jù))伤极,線程會重試操作,直到成功為止姨伤。
  4. 內(nèi)存屏障
    • 使用內(nèi)存屏障來確保在多處理器系統(tǒng)中內(nèi)存操作的順序性和可見性哨坪。
  5. 避免ABA問題
    • 使用版本號或標(biāo)記來解決CAS操作可能遇到的ABA問題,即在檢查和更新之間值被改變?nèi)缓笥肿兓卦档那闆r乍楚。
  6. 偽共享
    • 避免在多核處理器上因緩存行共享導(dǎo)致的性能問題当编,通過調(diào)整數(shù)據(jù)結(jié)構(gòu)的內(nèi)存布局來減少偽共享。
  7. 無鎖算法設(shè)計
    • 設(shè)計算法時徒溪,需要考慮所有可能的執(zhí)行路徑和線程間的交互忿偷,確保算法的正確性和性能。
  8. 測試和驗證
    • 無鎖數(shù)據(jù)結(jié)構(gòu)需要通過嚴(yán)格的測試和驗證來確保其在各種并發(fā)條件下的正確性和性能臊泌。

4.4. 樂觀鎖(Optimistic Locking)

場景描述:在分布式系統(tǒng)中鲤桥,配置信息需要被多個服務(wù)共享和更新。使用樂觀鎖可以減少鎖的競爭渠概,提高配置更新的效率茶凳。

public class DistributedConfig {
    private String config;
    private long version;

    public boolean updateConfig(String newConfig) {
        long currentVersion = version;
        if (version == currentVersion) {
            config = newConfig;
            version++;
            return true;
        }
        return false;
    }

    public String getConfig() {
        return config;
    }

    public long getVersion() {
        return version;
    }
}

4.5. 條件變量(Condition Variables)

場景描述:在任務(wù)調(diào)度系統(tǒng)中,任務(wù)可能需要等待某些條件滿足后才能執(zhí)行播揪。使用條件變量可以有效地管理這些條件的等待和通知贮喧。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.List;
import java.util.ArrayList;

public class TaskScheduler {
    private final List<Runnable> tasks = new ArrayList<>();
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void addTask(Runnable task) {
        lock.lock();
        try {
            tasks.add(task);
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public void executeTasks() {
        lock.lock();
        try {
            while (tasks.isEmpty()) {
                condition.await();
            }
            Runnable task = tasks.remove(0);
            task.run();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }
}
ReentrantLock 類設(shè)計
image.png
核心思路
  1. 可重入性
    • 允許同一線程多次獲得同一鎖,每次獲得鎖都需要對應(yīng)釋放一次猪狈。
  2. 公平性選擇
    • 可以選擇公平鎖或非公平鎖箱沦。公平鎖按照線程請求鎖的順序來分配,而非公平鎖則可能允許“插隊”罪裹。
  3. 鎖的靈活性
    • 提供了嘗試獲取鎖(tryLock)饱普、定時獲取鎖(tryLock with timeout)运挫、可中斷獲取鎖(lockInterruptibly)等操作。
  4. 條件變量支持
    • 可以與 Condition 接口配合使用套耕,提供更復(fù)雜的線程間協(xié)調(diào)功能谁帕。
實現(xiàn)機制
  1. 同步器 Sync
    • ReentrantLock 使用一個內(nèi)部的同步器(如 AQS - AbstractQueuedSynchronizer)來管理鎖的獲取和釋放。
  2. 鎖狀態(tài)
    • 維護(hù)一個狀態(tài)變量來表示鎖的持有者和鎖的重入次數(shù)冯袍。
  3. 線程調(diào)度
    • 當(dāng)鎖被占用時匈挖,請求鎖的線程會被放入同步隊列中等待。
  4. 鎖的獲取與釋放
    • 提供了獲取和釋放鎖的一系列方法康愤,包括重入的邏輯處理儡循。
  5. 條件對象
    • 可以為 ReentrantLock 創(chuàng)建一個或多個 Condition 對象,用于線程間的等待/通知操作征冷。
Condition Lock設(shè)計思路
image.png
核心思路
  1. 線程同步
    • 條件變量用于同步線程間的操作择膝,使得線程可以在某個條件成立之前掛起。
  2. 等待/通知機制
    • 線程可以在條件不滿足時掛起(等待)检激,并在條件滿足時被喚醒(通知)肴捉。
  3. 鎖的結(jié)合使用
    • 條件變量通常與互斥鎖(mutex)結(jié)合使用,以確保在等待條件變量時數(shù)據(jù)的一致性叔收。
  4. 減少忙等待
    • 通過掛起線程而不是忙等待齿穗,條件變量減少了CPU資源的浪費。
  5. 響應(yīng)性
    • 條件變量提供了一種機制饺律,允許線程在條件改變時迅速響應(yīng)窃页。
實現(xiàn)機制
  1. 等待隊列
    • 條件變量內(nèi)部通常有一個等待隊列,所有等待該條件的線程都會被放入這個隊列复濒。
  2. 鎖的獲取與釋放
    • 在調(diào)用條件變量的 wait() 方法前脖卖,線程必須持有相關(guān)的鎖。
    • 在線程等待條件變量時芝薇,它會釋放鎖胚嘲,以便其他線程可以訪問共享資源作儿。
    • 當(dāng)條件變量的 signal()broadcast() 被調(diào)用時洛二,等待的線程會被喚醒,并在重新嘗試獲取鎖后繼續(xù)執(zhí)行攻锰。
  3. 條件檢查
    • 線程在被喚醒后晾嘶,需要重新檢查條件是否滿足,因為喚醒不一定意味著條件已經(jīng)滿足娶吞。
  4. 避免虛假喚醒
    • 線程在被喚醒后必須檢查條件垒迂,以避免虛假喚醒,這是由于操作系統(tǒng)調(diào)度或其他原因?qū)е戮€程被喚醒妒蛇,但條件并未滿足机断。
  5. 支持多個條件
    • 條件變量可以與多個條件結(jié)合使用楷拳,允許線程等待多個條件中的任何一個。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吏奸,一起剝皮案震驚了整個濱河市欢揖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奋蔚,老刑警劉巖她混,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異泊碑,居然都是意外死亡坤按,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門馒过,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臭脓,“玉大人,你說我怎么就攤上這事腹忽⌒蝗担” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵留凭,是天一觀的道長佃扼。 經(jīng)常有香客問我,道長蔼夜,這世上最難降的妖魔是什么兼耀? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮求冷,結(jié)果婚禮上瘤运,老公的妹妹穿的比我還像新娘。我一直安慰自己匠题,他們只是感情好拯坟,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著韭山,像睡著了一般郁季。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钱磅,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天梦裂,我揣著相機與錄音,去河邊找鬼盖淡。 笑死年柠,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的褪迟。 我是一名探鬼主播冗恨,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼答憔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了掀抹?” 一聲冷哼從身側(cè)響起攀唯,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎渴丸,沒想到半個月后侯嘀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡谱轨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年戒幔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片土童。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡诗茎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出献汗,到底是詐尸還是另有隱情敢订,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布罢吃,位于F島的核電站楚午,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏尿招。R本人自食惡果不足惜矾柜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望就谜。 院中可真熱鬧怪蔑,春花似錦、人聲如沸丧荐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽虹统。三九已至弓坞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間窟却,已是汗流浹背昼丑。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夸赫,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓咖城,卻偏偏與公主長得像茬腿,于是被迫代替她去往敵國和親呼奢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容