溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

java中的ReetrantLock與鎖作用

發(fā)布時(shí)間:2021-07-09 09:06:50 來(lái)源:億速云 閱讀:132 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“java中的ReetrantLock與鎖作用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

java主要分兩類鎖,一種是synchronized關(guān)鍵字修飾的鎖,另一種是J.U.C.提供的鎖。J.U.C里核心鎖就是ReentrantLock

ReentrantLock(可重入鎖)和synchronized區(qū)別

  • 可重入性

  • 鎖的實(shí)現(xiàn),synchronized關(guān)鍵字是依賴于JVM實(shí)現(xiàn)的,而ReentrantLock是JDK實(shí)現(xiàn)的,這兩個(gè)有什么區(qū)別?說(shuō)白了就類似于操作系統(tǒng)來(lái)控制實(shí)現(xiàn)和用戶自己敲代碼實(shí)現(xiàn)的區(qū)別。synchronized實(shí)現(xiàn)依賴于JVM,很難查到源碼,而ReentrantLock可以通過(guò)閱讀源碼來(lái)實(shí)現(xiàn)。

  • 性能區(qū)別,自從synchronized引入偏向鎖、輕量級(jí)鎖(自旋鎖)之后性能差不多,官方建議使用synchronized。

  • 功能區(qū)別,synchronized使用更方便,由編譯器保證加鎖與釋放,而ReentrantLock需要手動(dòng)聲明加鎖與釋放鎖。

ReentrantLock獨(dú)有的功能:

  • 可指定公平鎖還是非公平鎖,而synchronized只能是非公平鎖。公平鎖:先等待的線程先獲得鎖。

  • 提供了一個(gè)Condition類,可以分組喚醒需要喚醒的線程。而不是像synchronized,隨機(jī)

  • 提供了一種能夠中斷等待鎖的線程的機(jī)制,lock.lockInterruptibly()。ReentrantLock是一種自旋鎖,通過(guò)循環(huán)調(diào)用CAS操作來(lái)實(shí)現(xiàn)加鎖,性能比較好也是因?yàn)楸苊饬耸咕€程進(jìn)入內(nèi)核態(tài)的阻塞狀態(tài),想盡辦法避免線程進(jìn)入內(nèi)核的阻塞狀態(tài),是我們分析和理解鎖設(shè)計(jì)的關(guān)鍵鑰匙

使用場(chǎng)景

如果需要ReentrantLock獨(dú)有的功能時(shí)可以使用ReentrantLock。其他情況下可以根據(jù)性能來(lái)選擇使用ReentrantLock還是synchronized。

synchronized能做的事情ReentrantLock都能做,而ReentrantLock能做的synchronize卻不一定能做,性能方面ReentrantLock也不必synchronize差。那么我們要不要拋棄synchronize?當(dāng)然是否定的,java.util.current.lock下的類一般用于高級(jí)用戶和高級(jí)情況的工具,一般來(lái)說(shuō)除非對(duì)lock的某個(gè)高級(jí)特性有明確的需要或者有明確的證據(jù)標(biāo)明在特定的情況下,同步已經(jīng)成為可伸縮性的瓶頸的時(shí)候,否則建議繼續(xù)使用synchronized。

即使對(duì)于這些高級(jí)的鎖定類來(lái)說(shuō),synchronized仍然有一些優(yōu)勢(shì),比如在使用synchronized時(shí)候不可能忘記釋放鎖,在退出synchronize塊時(shí),jvm會(huì)為你做這些事情,否則一旦忘記釋放鎖而產(chǎn)生死鎖很難查出原因,因此不建議初級(jí)開發(fā)人員使用lock。

另外當(dāng)jvm用synchronized來(lái)管理鎖定請(qǐng)求和釋放時(shí),jvm在生成線程轉(zhuǎn)儲(chǔ)時(shí),能夠包括鎖定信息,這些對(duì)調(diào)試非常有價(jià)值,因?yàn)樗麄兡軜?biāo)識(shí)死鎖或者其他異常行為的來(lái)源。而lock類只是一個(gè)普通的類,jvm不知道具體哪個(gè)線程擁有l(wèi)ock對(duì)象,而且?guī)缀趺總€(gè)開發(fā)人員都熟悉synchronized,可以在jvm的所有版本中工作,在大部分使用場(chǎng)景下都可以使用synchronized來(lái)實(shí)現(xiàn)。 

@Slf4j
public class LockExample2 {

    // 請(qǐng)求總數(shù)
    public static int clientTotal = 5000;

    // 同時(shí)并發(fā)執(zhí)行的線程數(shù)
    public static int threadTotal = 200;

    public static int count = 0;

    private final static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        //線程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定義信號(hào)量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定義計(jì)數(shù)器
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for(int i = 0; i < clientTotal; i++) {
            executorService.execute(() ->{
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }

    public static void add() {
        lock.lock();
        try{
            count++;
        }finally {
            lock.unlock();
        }
    }

}

ReentrantReadWriteLock

@Slf4j
public class LockExample3 {

    private final Map<String, Data> map = new TreeMap<>();
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public Data get (String key){
        readLock.lock();
        try{
            return map.get(key);
        } finally {
            readLock.unlock();
        }
    }
    public Set<String> getAllKeys() {
        readLock.lock();
        try{
            return map.keySet();
        }finally {
            readLock.unlock();
        }
    }
    public Data put(String key, Data value) {
        writeLock.lock();
        try {
            return map.put(key, value);
        }finally {
            writeLock.unlock();
        }
    }
    class Data {

    }

}

write.lock()保證在沒有讀鎖的時(shí)候才進(jìn)行寫入操作,對(duì)數(shù)據(jù)同步做的更多一些,使用悲觀讀取,也就是說(shuō)如果有寫入鎖時(shí),堅(jiān)決不允許有讀鎖還保持著,保證了在寫的時(shí)候其他事情已經(jīng)做完了。這樣就會(huì)有一個(gè)問(wèn)題,在讀取情況較多而寫入很少的時(shí)候,調(diào)用上面例子中的put方法就會(huì)遭遇饑餓(寫鎖一直想執(zhí)行,但是讀鎖一直在,導(dǎo)致寫鎖永遠(yuǎn)無(wú)法執(zhí)行,一直在等待)

StampedLock

stampedLock控制鎖有三種模式,分別是寫、讀、樂觀讀。StampedLock由版本和模式兩個(gè)模塊組成,返回一個(gè)數(shù)字(票據(jù)stamp),用相應(yīng)的鎖狀態(tài)來(lái)表示并控制相應(yīng)的訪問(wèn),數(shù)字0表示沒有寫鎖被訪問(wèn)。讀鎖分為樂觀鎖和悲觀鎖,樂觀鎖是當(dāng)讀的情況很多,寫的情況很少,可以認(rèn)為讀寫同時(shí)發(fā)生的幾率很小,因此不悲觀的使用完全悲觀的鎖定,程序可以讀取數(shù)據(jù)之后是否得到寫入執(zhí)行的變更,再采取后續(xù)的措施,這一個(gè)小小的改進(jìn),可以大幅度提高程序的吞吐量。

@Slf4j
public class LockExample4 {

    // 請(qǐng)求總數(shù)
    public static int clientTotal = 5000;

    // 同時(shí)并發(fā)執(zhí)行的線程數(shù)
    public static int threadTotal = 200;

    public static int count = 0;

    private final static StampedLock lock = new StampedLock();

    public static void main(String[] args) throws InterruptedException {
        //線程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        //定義信號(hào)量
        final Semaphore semaphore = new Semaphore(threadTotal);
        //定義計(jì)數(shù)器
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for(int i = 0; i < clientTotal; i++) {
            executorService.execute(() ->{
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (InterruptedException e) {

                    log.error("exception", e);
                }
                countDownLatch.countDown();

            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count);
    }

    public static void add() {
        long stamp = lock.writeLock();
        try{
            count++;
        }finally {
            lock.unlock(stamp);
        }
    }

}

總結(jié)

synchronized:是在jvm層面實(shí)現(xiàn)的,不但可以通過(guò)一些監(jiān)控工具監(jiān)控synchronized的鎖定,而且在代碼執(zhí)行時(shí)出現(xiàn)異常JVM也可以釋放鎖定,jvm會(huì)自動(dòng)加鎖與解鎖。

ReentrantLock、ReentrantReadWriteLock、StampedLock:都是對(duì)象層面的鎖定,要保證鎖一定會(huì)被釋放,一定要把unlock操作放在finally中才安全一些。StampedLock對(duì)吞度量有巨大的改進(jìn),特別是在讀線程很多的場(chǎng)景下。

那么我們?cè)撊绾芜x擇使用哪個(gè)鎖?

  1. 當(dāng)只有少量的競(jìng)爭(zhēng)者時(shí),synchronized是一個(gè)很好的通用鎖實(shí)現(xiàn)。

  2. 競(jìng)爭(zhēng)者較多,而且競(jìng)爭(zhēng)者的增長(zhǎng)趨勢(shì)可以預(yù)估,ReentrantLock是一個(gè)很好的通用鎖實(shí)現(xiàn)。

我們?cè)谟面i時(shí)不是看哪個(gè)鎖高級(jí)用哪個(gè)。適合自己使用場(chǎng)景的才是最關(guān)鍵的。 

需要注意的是synchronized不會(huì)引發(fā)死鎖,jvm會(huì)自動(dòng)解鎖。而其他的Lock一旦使用不當(dāng)會(huì)造成死鎖,有可能會(huì)在一些情況下未執(zhí)行unlock操作。

“java中的ReetrantLock與鎖作用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI