舉例說(shuō)明Java多線程編程中讀寫鎖的使用
以下示例為 java api并發(fā)庫(kù)中 ReentrantReadWriteLock自帶的實(shí)例,下面進(jìn)行解讀
class CachedData { Object data; volatile boolean cacheValid; ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); void processCachedData() { rwl.readLock().lock();//@1 if (!cacheValid) { // Must release read lock before acquiring write lock rwl.readLock().unlock();//@4 rwl.writeLock().lock();//@2 // Recheck state because another thread might have acquired // write lock and changed state before we did. if (!cacheValid) {//@3 data = ... cacheValid = true; } // Downgrade by acquiring read lock before releasing write lock rwl.readLock().lock(); rwl.writeLock().unlock(); // Unlock write, still hold read } use(data); rwl.readLock().unlock(); } }
當(dāng)有n多線程 使用同一CachedData 實(shí)例對(duì)象 調(diào)用processCachedData方法時(shí),就會(huì)產(chǎn)生線程的并發(fā)問(wèn)題.
@1行,當(dāng)有線程正在對(duì)數(shù)據(jù)進(jìn)行 寫操作的時(shí)候,運(yùn)行到@1行的線程要等待 寫操作的完成,因?yàn)榈谝粋€(gè)運(yùn)行到@2的線程會(huì)加上鎖,然后對(duì)數(shù)據(jù)進(jìn)行需該,期間不允許任何線程進(jìn)行讀或者是寫的操作,
當(dāng)寫完后,在該線程上加上讀鎖操作,以防止解寫鎖后,別的線程對(duì)數(shù)據(jù)再次進(jìn)行寫時(shí)出錯(cuò).在第一個(gè)運(yùn)行到@2的線程之后的很多線程,
可能已經(jīng)運(yùn)行到了@4,當(dāng)對(duì)數(shù)據(jù)修改好之后,解除掉寫鎖,別的線程就會(huì)執(zhí)行到@2,這時(shí)第一個(gè)線程已經(jīng)經(jīng)數(shù)據(jù)修改好了,所以有了@3的判斷。
在編寫多線程程序的時(shí)候,要置于并發(fā)線程的環(huán)境下考慮,巧妙的運(yùn)用ReentrantReadWriteLock,在運(yùn)用時(shí),注意鎖的降級(jí),寫入鎖可以獲得讀鎖,讀鎖不可以獲得寫入鎖,所以在上寫入鎖時(shí),必須先將讀鎖進(jìn)行解除,然后上讀鎖。
使用時(shí)注意的幾個(gè)方面:
讀鎖是排寫鎖操作的,讀鎖不排讀鎖操作,多個(gè)讀鎖可以并發(fā)不阻塞。即在讀鎖獲取后和讀鎖釋放之前,寫鎖并不能被任何線程獲得,
多個(gè)讀鎖同時(shí)作用期間,試圖獲取寫鎖的線程都處于等待狀態(tài),當(dāng)最后一個(gè)讀鎖釋放后,試圖獲取寫鎖的線程才有機(jī)會(huì)獲取寫鎖。
寫鎖是排寫鎖、排讀鎖操作的。當(dāng)一個(gè)線程獲取到寫鎖之后,其他試圖獲取寫鎖和試圖獲取讀鎖的線程都處于等待狀態(tài),直到寫鎖被釋放。
寫鎖是可以獲得讀鎖的,即:
rwl.writeLock().lock(); //在寫鎖狀態(tài)中,可以獲取讀鎖 rwl.readLock().lock(); rwl.writeLock().unlock();
讀鎖是不能夠獲得寫鎖的,如果要加寫鎖,本線程必須釋放所持有的讀鎖,即:
rwl.readLock().lock(); //...... //必須釋放掉讀鎖,才能夠加寫鎖 rwl.readLock().unlock(); rwl.writeLock().lock();
讀寫鎖是線程讀寫同一文件所需要用到的,讀寫鎖是什么東西在這里不做過(guò)多的解釋,可以自己去百度或谷歌去搜一下。
謹(jǐn)在此附上我自己寫的緩存系統(tǒng)的簡(jiǎn)單實(shí)現(xiàn),你從中也能悟出緩存實(shí)現(xiàn)的基本思想
緩存里面有數(shù)據(jù)就從緩存中取,沒(méi)有就給你從其他地方得到。
package cn.com.scl.cache import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 緩存的實(shí)現(xiàn),每個(gè)線程只能獲得他自己的緩存,也應(yīng)該是單例的 * 本類沒(méi)有去實(shí)現(xiàn)單例,如果需要的話可以自行去實(shí)現(xiàn) * @author scl * */ public class CacheSystem { private Map<String, Object> cache = new HashMap<String,Object>(); private ReadWriteLock rwl = new ReentrantReadWriteLock(); public Object getData(String key){ //先從緩存中去取數(shù)據(jù),先加上讀鎖 rwl.readLock().lock(); Object obj = null; try{ obj = cache.get(key); if(obj == null){ //先解除讀鎖,在上寫鎖(必須先解除讀鎖才能成功上寫鎖) rwl.readLock().unlock(); rwl.writeLock().lock(); //去數(shù)據(jù)庫(kù)取數(shù)據(jù),再判斷一次是否為null,因?yàn)橛锌赡芏鄠€(gè)線程獲得寫鎖 try{ if(obj == null){ obj = new String("obj is get from db"); } }finally{ //先上讀鎖,然后再解除寫鎖(這樣可以成功完成,在解除寫鎖前獲得讀鎖,寫鎖被降級(jí)--這翻譯的api上的) rwl.readLock().lock(); rwl.writeLock().unlock();//解除寫鎖,讀鎖仍然持有 } } }finally{ rwl.readLock().unlock(); } return obj; } }
相關(guān)文章
C# 實(shí)現(xiàn)對(duì)PPT文檔加密、解密及重置密碼的操作方法
這篇文章主要介紹了C# 實(shí)現(xiàn)對(duì)PPT文檔加密、解密及重置密碼的操作方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-11-11在C#中List集合使用First()方法獲取第一個(gè)元素的操作
這篇文章主要介紹了在C#中List集合使用First()方法獲取第一個(gè)元素的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12C#向線程中傳遞多個(gè)參數(shù)的解決方法(兩種)
這篇文章主要介紹了C#向線程中傳遞多個(gè)參數(shù)的解決方法(兩種)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07DevExpress實(shí)現(xiàn)為TextEdit設(shè)置水印文字的方法
這篇文章主要介紹了DevExpress實(shí)現(xiàn)為TextEdit設(shè)置水印文字的方法,對(duì)C#程序設(shè)計(jì)人員來(lái)說(shuō)是一個(gè)很實(shí)用的技巧,需要的朋友可以參考下2014-08-08c#讀寫App.config,ConfigurationManager.AppSettings 不生效的解決方法
這篇文章主要介紹了c#讀寫App.config,ConfigurationManager.AppSettings 不生效的解決方法,需要的朋友可以參考下2015-10-10C#創(chuàng)建Windows Service(Windows 服務(wù))的方法步驟
本文介紹了如何用C#創(chuàng)建、安裝、啟動(dòng)、監(jiān)控、卸載簡(jiǎn)單的Windows Service 的內(nèi)容步驟和注意事項(xiàng),具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11C#中多態(tài)現(xiàn)象和多態(tài)的實(shí)現(xiàn)方法
這篇文章主要介紹了C#中多態(tài)現(xiàn)象和多態(tài)的實(shí)現(xiàn)方法,較為詳細(xì)的分析了多態(tài)的原理與C#實(shí)現(xiàn)多態(tài)的方法,以及相關(guān)的注意事項(xiàng),需要的朋友可以參考下2015-05-05C#模擬實(shí)現(xiàn)抽獎(jiǎng)小程序的示例代碼
這篇文章主要介紹了通過(guò)C#模擬實(shí)現(xiàn)一個(gè)簡(jiǎn)單的抽獎(jiǎng)小程序,文中的示例代碼講解詳細(xì),對(duì)我們了解C#有一定的幫助,需要的可以參考一下2021-12-12c#使用IAsyncEnumerable實(shí)現(xiàn)流式分段傳輸
這篇文章主要為大家詳細(xì)介紹了c#如何使用IAsyncEnumerable實(shí)現(xiàn)流式分段傳輸,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-10-10