Java中的ReentrantReadWriteLock實現(xiàn)原理詳解
介紹
讀寫鎖
- 實現(xiàn)了接口ReadWriteLock
- 適合于讀多寫少的情況
- 支持公平鎖和非公平鎖
- 支持可沖入(進(jìn)入讀鎖后可再進(jìn)入讀鎖,進(jìn)入寫鎖后可再進(jìn)入寫鎖和讀鎖)
- 支持可沖入和公平與非公平
缺點(diǎn)
(1) 寫鎖饑餓問題,讀的線程很多,寫的線程搶占不到鎖,就一直搶占不到鎖,就饑餓
(2) 鎖降級,獲取寫鎖后又再次獲取讀鎖(重入),釋放了寫鎖之后就變成了讀鎖,就是鎖降級
內(nèi)部接口
Sync/ReadLock/WriteLock/FairSync/NonfairSync 是其內(nèi)部類 下面圖有問題,ReadWriteLock沒有實現(xiàn)Lock
代碼演示
public class Lock { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { Lock.put(i + "", i + ""); } } }).start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { Lock.get(i + ""); } } }).start(); } static Map<String, Object> map = new HashMap<String, Object>(); static ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); static java.util.concurrent.locks.Lock readLock = readWriteLock.readLock(); static java.util.concurrent.locks.Lock writeLock = readWriteLock.writeLock(); public static final Object get(String key) { readLock.lock(); try { System.out.println("正在做讀的操作,key:" + key + "開始"); Thread.sleep(100); Object object = map.get(key); System.out.println("正在做讀的操作,key:" + key + "結(jié)束"); System.out.println(); return object; } catch (InterruptedException e) { e.printStackTrace(); } finally { readLock.unlock(); } return key; } public static final Object put(String key, Object value) { writeLock.lock(); try { System.out.println("正在做寫的操作,key:" + key + ",value:" + value + "開始"); Thread.sleep(100); Object object = map.put(key, value); System.out.println("正在做寫的操作,key:" + key + ",value:" + value + "結(jié)束"); System.out.println(); return object; } catch (InterruptedException e) { e.printStackTrace(); } finally { writeLock.unlock(); } return value; } public static final void clear() { writeLock.lock(); try { map.clear(); } finally { writeLock.unlock(); } } }
class MyResource { Map<String,String> map = new HashMap<>(); Lock lock = new ReentrantLock(); ReadWriteLock rwLock = new ReentrantReadWriteLock(); public void write(String key ,String value) { rwLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName()+"\t"+"正在寫入"); map.put(key,value); //暫停毫秒 try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"\t"+"完成寫入"); }finally { rwLock.writeLock().unlock(); } } public void read(String key) { rwLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName()+"\t"+"正在讀取"); String result = map.get(key); // 暫停2000毫秒,演示讀鎖沒有完成之前,寫鎖無法獲得 try { TimeUnit.MILLISECONDS.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"\t"+"完成讀取"+"\t"+result); }finally { rwLock.readLock().unlock(); } } } /** * @auther zzyy * @create 2022-04-08 18:18 */ public class ReentrantReadWriteLockDemo { public static void main(String[] args) { MyResource myResource = new MyResource(); for (int i = 1; i <=10; i++) { int finalI = i; new Thread(() -> { myResource.write(finalI +"", finalI +""); }, String.valueOf(i)).start(); } for (int i = 1; i <=10; i++) { int finalI = i; new Thread(() -> { myResource.read(finalI +""); },String.valueOf(i)).start(); } // 暫停幾秒鐘線程 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 在讀鎖沒有完成時, 寫鎖不能寫入 for (int i = 1; i <=3; i++) { int finalI = i; new Thread(() -> { myResource.write(finalI +"", finalI +""); },"新寫鎖線程->"+String.valueOf(i)).start(); } } }
實現(xiàn)原理
從表面來看,ReadLock和WriteLock是兩把鎖,實際上它只是同一把鎖的兩個視圖而已。
什么叫兩個視圖呢?可以理解為是一把鎖,線程分成兩類:讀線程和寫線程。讀線程和寫線程之間不互斥(可以同時拿到這把鎖),讀線程之間不互斥,寫線程之間互斥
從下面的構(gòu)造方法也可以看出,readerLock和writerLock實際共用同一個sync對象。
sync對象同互斥鎖一樣,分為非公平和公平兩種策略,并繼承自AQS
public ReentrantReadWriteLock() { this(false); } public ReentrantReadWriteLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); // fair為false使用非公平鎖,true使用公平鎖 readerLock = new ReadLock(this); writerLock = new WriteLock(this); }
同互斥鎖一樣,讀寫鎖也是用state變量來表示鎖狀態(tài)的。只是state變量在這里的含義和互斥鎖完全不同。在內(nèi)部類Sync中,對state變量進(jìn)行了重新定義,如下所示:
abstract static class Sync extends AbstractQueuedSynchronizer { // ... static final int SHARED_SHIFT = 16; static final int SHARED_UNIT = (1 << SHARED_SHIFT); static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 持有讀鎖的線程的重入次數(shù) static int sharedCount(int c) { return c >>> SHARED_SHIFT; } // 持有寫鎖的線程的重入次數(shù) static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; } // ... }
把 state變量拆成兩半,低16位用來記錄寫鎖。但同一時間既然只能有一個線程寫,為什么還需要16位呢?這是因為一個寫線程可能多次重入。
例如,低16位的值等于5,表示一個寫線程重入了5次 高16位,用來讀鎖。
如高16位的值等于5,既可以表示5個讀線程都拿到了該鎖;也可以表示一個讀線程重入了5次
為什么要把一個int類型變量拆成兩半,而不是用兩個int型變量分別表示讀鎖和寫鎖的狀態(tài)呢?因為無法用一次CAS同時操作兩個int變量,所以用了一個int型的高16位和低16位分別表示讀鎖和寫鎖的狀態(tài)
當(dāng)state=0時,說明既沒有線程持有讀鎖,也沒有線程持有寫鎖;當(dāng)state != 0時,要么有線程持有讀鎖,要么有線程持有寫鎖,兩者不能同時成立,因為讀和寫互斥。
這時再進(jìn)一步通過sharedCount(state)和exclusiveCount(state)判斷到底是讀線程還是寫線程持有了該鎖
到此這篇關(guān)于Java中的ReentrantReadWriteLock實現(xiàn)原理詳解的文章就介紹到這了,更多相關(guān)ReentrantReadWriteLock原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
microlog4android將Android Log日志寫到SD卡文件中實現(xiàn)方法
這篇文章主要介紹了microlog4android將Android Log日志寫到SD卡文件中實現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下2016-10-10Spring實現(xiàn)在非controller中獲取request對象
這篇文章主要介紹了Spring實現(xiàn)在非controller中獲取request對象方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Spring Boot自定義Banner實現(xiàn)代碼
這篇文章主要介紹了Spring Boot自定義Banner實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-01-01mybatis-xml映射文件及mybatis動態(tài)sql詳解
XML映射文件的名稱與Mapper接口名稱一致,并且將XML映射文件和Mapper接口放置在相同包下(同包同名),這篇文章主要介紹了mybatis-xml映射文件及mybatis動態(tài)sql的相關(guān)知識,感興趣的朋友跟隨小編一起看看吧2024-12-12SpringBoot部署在tomcat容器中運(yùn)行的部署方法
這篇文章主要介紹了SpringBoot部署在tomcat容器中運(yùn)行的部署方法,需要的朋友可以參考下2018-10-10Java8 Stream中對集合數(shù)據(jù)進(jìn)行快速匹配和賦值的代碼示例
這篇文章主要介紹了Java8 Stream中如何對集合數(shù)據(jù)進(jìn)行快速匹配和賦值,文中通過代碼示例為大家介紹的非常詳細(xì),具有一定的參考價值,需要的朋友可以參考下2023-06-06Spring七大事務(wù)傳遞機(jī)制深入分析實現(xiàn)原理
實際項目開發(fā)中,如果涉及到多張表操作時,為了保證業(yè)務(wù)數(shù)據(jù)的一致性,大家一般都會采用事務(wù)機(jī)制,好多小伙伴可能只是簡單了解一下,遇到事務(wù)失效的情況,便會無從下手,下面這篇文章主要給大家介紹了關(guān)于Spring事務(wù)傳遞機(jī)制的相關(guān)資料,需要的朋友可以參考下2023-03-03