最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 中通過 key 獲取鎖的方法

 更新時間:2022年11月15日 09:33:08   作者:明明如月學(xué)長  
這篇文章主要介紹了Java 中通過 key 獲取鎖,本文演示如何對某個 key 加鎖,以保證對該 key 的并發(fā)操作限制,可以實現(xiàn)同一個 key 一個或者多個線程同時執(zhí)行,需要的朋友可以參考下

一、概覽

本文我們將了解如何通過特定鍵獲取鎖,以保證該鍵上的操作的線程安全,并且不妨礙其他鍵。
一般來說,我們需要實現(xiàn)兩個方法:

void lock(String key)
void unlock(String key)

本文以字符串作為鍵為例,大家可以根據(jù)實際需要改造成任意類型的鍵,重寫 equas 和 hashCode 方法,保證唯一性即可。

二、簡單的互斥鎖

假設(shè)需要滿足當(dāng)前線程獲取鎖則需要執(zhí)行特定代碼,否則不執(zhí)行這個場景。
我們可以維護一系列 Key 的 Set, 在使用時添加到 Set 中,解鎖時移除對應(yīng)的 Key。
此時,需要考慮線程安全問題。因此需要使用線程安全的 Set 實現(xiàn),如基于 ConcurrentHashMap 的線程安全 Set。

public class SimpleExclusiveLockByKey {

    private static Set<String> usedKeys= ConcurrentHashMap.newKeySet();
    
    public boolean tryLock(String key) {
        return usedKeys.add(key);
    }
    
    public void unlock(String key) {
        usedKeys.remove(key);
    }

}

使用案例:

String key = "key";
SimpleExclusiveLockByKey lockByKey = new SimpleExclusiveLockByKey();
try {
    lockByKey.tryLock(key);
    // 在這里添加對該 key 獲取鎖之后要執(zhí)行的代碼
} finally { // 非常關(guān)鍵
    lockByKey.unlock(key);
}

注意一定要在 finally 代碼塊中解鎖,以保證即便發(fā)生異常時,也可以正常解鎖。

三、按鍵來獲取和釋放鎖

以上代碼可以保證獲取鎖后才執(zhí)行,但無法實現(xiàn)未拿到鎖的線程等待的效果。
有時候,我們需要讓未獲取到對應(yīng)鎖的線程等待。
流程如下:

  • 第一個線程獲取某個 key 的鎖
  • 第二個線程獲取同一個 key 的鎖,第二個線程需要等待
  • 第一個線程釋放某個 key 的鎖
  • 第二個線程獲取該 key 的鎖,然后執(zhí)行其代碼

3.1 使用線程計數(shù)器定義 Lock

我們可以使用 ReentrantLock 來實行線程阻塞。
我們通過內(nèi)部類來封裝 Lock。該類統(tǒng)計某個 key 上執(zhí)行的線程數(shù)。暴露兩個方法,一個是線程數(shù)增加,一個是減少線程數(shù)。

private static class LockWrapper {
    private final Lock lock = new ReentrantLock();
    private final AtomicInteger numberOfThreadsInQueue = new AtomicInteger(1);

    private LockWrapper addThreadInQueue() {
        numberOfThreadsInQueue.incrementAndGet(); 
        return this;
    }

    private int removeThreadFromQueue() {
        return numberOfThreadsInQueue.decrementAndGet(); 
    }

}

3.2 處理排隊的線程

接下來繼續(xù)使用 ConcurrentHashMap , key 作為鍵, LockWrapper 作為值。
保證同一個 key 使用同一個 LockWrapper 中的同一把鎖。

private static ConcurrentHashMap<String, LockWrapper> locks = new ConcurrentHashMap<String, LockWrapper>();

一個線程想要獲取某個 key 的鎖時,需要看該 key 對應(yīng)的 LockWrapper 是否已經(jīng)存在。

  • 如果不存在,創(chuàng)建一個 LockWrapper ,計數(shù)器設(shè)置為1
  • 如果存在,對應(yīng)的 LockWrapper 加1
public void lock(String key) {
    LockWrapper lockWrapper = locks.compute(key, (k, v) -> v == null ? new LockWrapper() : v.addThreadInQueue());
    lockWrapper.lock.lock();
}

3.3 解鎖和移除 Entry

解鎖時將等待的隊列減一。
當(dāng)前 key 對應(yīng)的線程數(shù)為 0 時,可以將其從 ConcurrentHashMap 中移除。

public void unlock(String key) {
    LockWrapper lockWrapper = locks.get(key);
    lockWrapper.lock.unlock();
    if (lockWrapper.removeThreadFromQueue() == 0) { 
        // NB : We pass in the specific value to remove to handle the case where another thread would queue right before the removal
        locks.remove(key, lockWrapper);
    }
}

3.4 總結(jié)

最終效果如下:

public class LockByKey {
    
    private static class LockWrapper {
        private final Lock lock = new ReentrantLock();
        private final AtomicInteger numberOfThreadsInQueue = new AtomicInteger(1);
        
        private LockWrapper addThreadInQueue() {
            numberOfThreadsInQueue.incrementAndGet(); 
            return this;
        }
        
        private int removeThreadFromQueue() {
            return numberOfThreadsInQueue.decrementAndGet(); 
        }
        
    }
    
    private static ConcurrentHashMap<String, LockWrapper> locks = new ConcurrentHashMap<String, LockWrapper>();
    
    public void lock(String key) {
        LockWrapper lockWrapper = locks.compute(key, (k, v) -> v == null ? new LockWrapper() : v.addThreadInQueue());
        lockWrapper.lock.lock();
    }
    
    public void unlock(String key) {
        LockWrapper lockWrapper = locks.get(key);
        lockWrapper.lock.unlock();
        if (lockWrapper.removeThreadFromQueue() == 0) { 
            // NB : We pass in the specific value to remove to handle the case where another thread would queue right before the removal
            locks.remove(key, lockWrapper);
        }
    }
}

使用示例:

String key = "key"; 
LockByKey lockByKey = new LockByKey(); 
try { 
    lockByKey.lock(key);
    // insert your code here 
} finally { // CRUCIAL 
    lockByKey.unlock(key); 
}

四、允許同一個 key 同時多個線程運行

我們還需要考慮另外一種場景: 前面對于同一個 key 同一時刻只允許一個線程執(zhí)行。如果我們想實現(xiàn),對于同一個 key ,允許同時運行 n 個線程該怎么辦?
為了方便理解,我們假設(shè)同一個 key 允許兩個線程。

  • 第一個線程想要獲取 某個 key 的鎖,允許
  • 第二個線程也想要獲取該 key 的鎖,允許
  • 第三個線程也想獲取該 key 的鎖,該線程需要等待第一個或第二個線程釋放鎖之后才可以執(zhí)行

Semaphore 很適合這種場景。Semaphore 可以控制同時運行的線程數(shù)。

public class SimultaneousEntriesLockByKey {

    private static final int ALLOWED_THREADS = 2;
    
    private static ConcurrentHashMap<String, Semaphore> semaphores = new ConcurrentHashMap<String, Semaphore>();
    
    public void lock(String key) {
        Semaphore semaphore = semaphores.compute(key, (k, v) -> v == null ? new Semaphore(ALLOWED_THREADS) : v);
        semaphore.acquireUninterruptibly();
    }
    
    public void unlock(String key) {
        Semaphore semaphore = semaphores.get(key);
        semaphore.release();
        if (semaphore.availablePermits() == ALLOWED_THREADS) { 
            semaphores.remove(key, semaphore);
        }  
    }
    
}

使用案例:

String key = "key"; 
SimultaneousEntriesLockByKey lockByKey = new SimultaneousEntriesLockByKey(); 
try { 
    lockByKey.lock(key); 
    // 在這里添加對該 key 獲取鎖之后要執(zhí)行的代碼
} finally { // 非常關(guān)鍵
    lockByKey.unlock(key); 
}

五、結(jié)論

本文演示如何對某個 key 加鎖,以保證對該 key 的并發(fā)操作限制,可以實現(xiàn)同一個 key 一個或者多個線程同時執(zhí)行。
相關(guān)代碼:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-concurrency-advanced-4

原文:https://www.baeldung.com/java-acquire-lock-by-key

到此這篇關(guān)于Java 中通過 key 獲取鎖的文章就介紹到這了,更多相關(guān)java 獲取鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java外觀模式解讀,讓你的代碼優(yōu)雅又高效

    Java外觀模式解讀,讓你的代碼優(yōu)雅又高效

    外觀模式(Facade?Pattern)是一種常用的結(jié)構(gòu)型設(shè)計模式,它為復(fù)雜的子系統(tǒng)提供一個簡單的接口,隱藏復(fù)雜的實現(xiàn)細節(jié),本文就來講講它是如何簡化代碼,提高可維護性的
    2023-05-05
  • Scala 操作Redis使用連接池工具類RedisUtil

    Scala 操作Redis使用連接池工具類RedisUtil

    這篇文章主要介紹了Scala 操作Redis使用連接池工具類RedisUtil,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Java異常分類以及幾種處理機制分析講解

    Java異常分類以及幾種處理機制分析講解

    在Java的廣闊宇宙中,有一群特殊的“超級英雄”,它們在代碼世界中穿梭,守護著程序的正常運行——它們就是“異?!?這些英雄們,各司其職,保護著程序免受錯誤的侵擾,今天,我們將深入這個神秘的世界,全面解析異常的分類,掌握異常的處理機制
    2024-07-07
  • new出來的對象中無法使用@autowired進行對象bean注入問題

    new出來的對象中無法使用@autowired進行對象bean注入問題

    這篇文章主要介紹了基于new出來的對象中無法使用@autowired進行對象bean注入問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 詳解關(guān)于springboot-actuator監(jiān)控的401無權(quán)限訪問

    詳解關(guān)于springboot-actuator監(jiān)控的401無權(quán)限訪問

    本篇文章主要介紹了詳解關(guān)于springboot-actuator監(jiān)控的401無權(quán)限訪問,非常具有實用價值,有興趣的可以了解一下
    2017-09-09
  • Python__雙劃線參數(shù)代碼實例解析

    Python__雙劃線參數(shù)代碼實例解析

    這篇文章主要介紹了python__雙劃線參數(shù)代碼實例解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • Spring Boot中使用RabbitMQ的示例代碼

    Spring Boot中使用RabbitMQ的示例代碼

    本篇文章主要介紹了Spring Boot中使用RabbitMQ的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-04-04
  • 10本Java架構(gòu)師必讀書籍

    10本Java架構(gòu)師必讀書籍

    這篇文章主要介紹了10本Java架構(gòu)師必讀書籍,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • 使用IDEA工具配置和運行vue項目及遇到的坑

    使用IDEA工具配置和運行vue項目及遇到的坑

    這篇文章主要介紹了使用IDEA工具配置和運行vue項目及遇到的坑,需要的朋友可以參考下
    2018-09-09
  • Java利用apache ftp工具實現(xiàn)文件上傳下載和刪除功能

    Java利用apache ftp工具實現(xiàn)文件上傳下載和刪除功能

    這篇文章主要為大家詳細介紹了Java利用apache ftp工具實現(xiàn)文件上傳下載、刪除功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06

最新評論

高邮市| 孝义市| 德州市| 大安市| 台北市| 普宁市| 泸西县| 泊头市| 灵石县| 门源| 东山县| 石屏县| 宝丰县| 郎溪县| 塘沽区| 讷河市| 四会市| 永清县| 绵竹市| 白河县| 抚顺市| 屏南县| 积石山| 涡阳县| 南皮县| SHOW| 汝南县| 丹阳市| 木兰县| 乐至县| 天气| 莱州市| 长顺县| 房产| 呼玛县| 临城县| 景德镇市| 江陵县| 德州市| 柳州市| 宽城|