您好,登錄后才能下訂單哦!
本篇內(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)鍵鑰匙
如果需要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(); } } }
@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由版本和模式兩個(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); } } }
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)景下。
當(dāng)只有少量的競(jìng)爭(zhēng)者時(shí),synchronized是一個(gè)很好的通用鎖實(shí)現(xiàn)。
競(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í)用文章!
免責(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)容。