Java讀寫鎖ReadWriteLock
一、讀寫鎖ReadWriteLock
ReadWriteLock 是 JDK 中的讀寫鎖介面,提供了 readLock 和 writeLock 兩種鎖的操作機制,一個是讀鎖,一個是寫鎖。
ReadWriteLock同Lock一樣也是一個介面,ReentrantLock 是Lock的一種實現,ReentrantReadWriteLock 是 ReadWriteLock 的一種實現。
ReadWriteLock 中只有寫鎖支援Condition,讀鎖不支援,讀鎖呼叫 newCondition() 方法,會丟擲 UnsupportedOperationException 異常
讀寫鎖非常適合讀多寫少的場景。讀寫鎖與互斥鎖的一個重要區別是讀寫鎖允許多個執行緒同時讀共享變數,這是讀寫鎖在讀多寫少的情況下效能較高的原因。
1、讀寫鎖的原則:
多個執行緒可同時讀共享變數
只允許一個執行緒寫共享變數
寫執行緒正在執行寫操作,禁止其他執行緒讀寫共享變數
2、讀寫鎖互斥原則:
讀-讀能共存,
讀-寫不能共存,
寫-寫不能共存。
例子:
public class ReadWriteLockDemo {
final static ReadWriteLock rwLock = new ReentrantReadWriteLock();
final static Lock readLock = rwLock.readLock();//讀鎖
final static Lock writeLock = rwLock.writeLock();//寫鎖
static int count = 0;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "==" + get());
}, "read").start();
}
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "==add");
add(5);
}, "write").start();
}
}
private static int get() {
//使用讀鎖
readLock.lock();
try {
return count;
} finally {
readLock.unlock();
}
}
private static void add(int num) {
//使用寫鎖
writeLock.lock();
try {
count = count + num;
} finally {
writeLock.unlock();
}
}
}
二、鎖的一些知識點
1、公平鎖和非公平鎖
概念
公平鎖:是指多個執行緒按照申請鎖的順序來獲取鎖,類似排隊打飯,先來後到。
非公平鎖:是指多個執行緒獲取鎖的順序並不是按照申請鎖的順序,有可能後申請的執行緒比先申請的執行緒優先獲取鎖,在高併發的情況下,有可能會造成優先順序反轉或者飢餓現象
公平鎖和非公平鎖區別
公平鎖:在併發壞境中.每個執行緒在獲取鎖時會先檢視此鎖維護的等待佇列,如果為空,或者當前執行緒是等待佇列的第一個,就佔有鎖.否則就會加入到等待佇列中.以後會按照FIFO的規則從佇列中取到自己。
非公平鎖:非公平鎖比較粗魯,上來就直接嘗試佔有鎖,如果嘗試失敗,就再採用類似公平鎖那種方式。
ReentrantLock 和 ReadWriteLock 而言,通過建構函式指定該鎖是否為公平鎖,預設是非公平鎖。非公平鎖的優點在於吞吐量比公平鎖大。
synchronized 而言,也是一種非公平鎖。
2、可重入鎖
可重入鎖,也叫做遞迴鎖,指的是同一執行緒外層函式獲得鎖之後 ,內層遞迴函式仍然有獲取該鎖的程式碼,但不受影響。
可重入鎖最大的作用是避免死鎖
可重入特性還允許從寫鎖降級到讀鎖—通過獲取寫鎖,然後獲取讀鎖,然後釋放寫鎖。但是,從讀鎖到寫鎖的升級是不可能的。
3、鎖降級
鎖降級指的是把持住(當前擁有的)寫鎖,再獲取到讀鎖,隨後釋放(先前有用的)寫鎖的過程。通過這種重入,可以減少一步流程(釋放寫鎖後 再次 獲取讀鎖)。使用了鎖降級,就可以減去釋放寫鎖的步驟。直接獲取讀鎖。效率更高。
當前執行緒擁有寫鎖,然後將其釋放,最後再獲取讀鎖,這種並不能稱之為鎖降級,
例項:
public class CachedData {
//模擬共享資料
private String data = "原來的資料";
//volatile修飾,保持記憶體可見性,資料是不是最新的
volatile boolean isUpdate;
//可重入讀寫鎖
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
// 模擬放置資料到快取
public void processCachedData(String d) {
//獲取寫鎖之前,首先獲取讀鎖
rwl.readLock().lock();
//發現資料不是最新的則放棄讀鎖(讀鎖不能升級),獲取寫鎖
if (!isUpdate) {
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
if (!isUpdate) {
data = d; //拿到寫鎖後,把新的資料寫入
isUpdate = true;
}
rwl.readLock().lock(); //擁有寫鎖的請況下金額直接獲取讀鎖
} finally {
//同時擁有讀鎖和寫鎖,在這裡釋放寫鎖,進行鎖降級
rwl.writeLock().unlock();
}
}
try {
System.out.println("最新的資料列印:" + data);
} finally {
rwl.readLock().unlock();
}
}
public static void main(String[] args) {
CachedData cachedData = new CachedData();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
cachedData.processCachedData(Thread.currentThread().getName() + "=新資料");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
通常可以在集合使用場景中看到ReentrantReadWriteLock的使用。不過只有在集合比較大,讀操作比寫操作多,操作開銷大於同步開銷的時候才是值得的。
public class ReadWriteLockDemo2 {
private final Map<String, Object> m = new TreeMap<String, Object>();
//讀寫鎖
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
//獲取讀鎖
private final Lock r = rwl.readLock();
//獲取寫鎖
private final Lock w = rwl.writeLock();
public Object get(String key) {
r.lock();
try {
return m.get(key);
} finally {
r.unlock();
}
}
public String[] allKeys() {
r.lock();
try {
Set<String> rsSet = m.keySet();
return rsSet.toArray(new String[rsSet.size()]);
} finally {
r.unlock();
}
}
public Object put(String key, Object value) {
w.lock();
try {
return m.put(key, value);
} finally {
w.unlock();
}
}
public void clear() {
w.lock();
try {
m.clear();
} finally {
w.unlock();
}
}
}
—— Stay Hungry. Stay Foolish. 求知若飢,虛心若愚。
相關文章
- Java 讀寫鎖 ReadWriteLock 原理與應用場景詳解Java
- ReadWriteLock讀寫鎖升級的踩坑:Kotlin作弊,最好使用StampedLock - javaspecialistsKotlinJava
- Java中的讀/寫鎖Java
- Java併發——讀寫鎖ReentrantReadWriteLockJava
- 鎖——Lock、Condition、ReadWriteLock、LockSupport
- 淺談Java中的鎖:Synchronized、重入鎖、讀寫鎖Javasynchronized
- Java 讀寫鎖 ReentrantReadWriteLock 原始碼分析Java原始碼
- Concurrency(十五: Java中的讀寫鎖)Java
- Java併發-顯式鎖篇【可重入鎖+讀寫鎖】Java
- 讀寫鎖
- 讀寫鎖 ReentrantReadWriteLock
- Java併發程式設計-讀寫鎖(ReentrantReadWriteLock)Java程式設計
- Java併發指南10:Java 讀寫鎖 ReentrantReadWriteLock 原始碼分析Java原始碼
- Lock鎖之重入鎖與讀寫鎖
- Java併發程式設計之鎖機制之ReentrantReadWriteLock(讀寫鎖)Java程式設計
- Java併發(8)- 讀寫鎖中的效能之王:StampedLockJava
- 【java併發程式設計】ReentrantLock 可重入讀寫鎖Java程式設計ReentrantLock
- Lock介面、重入鎖ReentrantLock、讀寫鎖ReentrantReadWriteLockReentrantLock
- 讀寫鎖 ReentrantReadWriteLock 與 互斥鎖 的效率
- MySQL MyISAM引擎的讀鎖與寫鎖MySql
- Go語言之讀寫鎖Go
- java併發程式設計-StampedLock高效能讀寫鎖Java程式設計
- Java併發基礎-鎖的使用及原理(可重入鎖、讀寫鎖、內建鎖、訊號量等)Java
- 深入理解讀寫鎖ReentrantReadWriteLock
- c++中的讀寫鎖C++
- JUC之讀寫鎖問題
- ReentrantReadWriteLock讀寫鎖及其在 RxCach
- Java中的讀寫鎖ReentrantReadWriteLock詳解,存在一個小缺陷Java
- Java併發包原始碼學習系列:ReentrantReadWriteLock讀寫鎖解析Java原始碼
- 併發技術4:讀寫鎖
- 原始碼分析:ReentrantReadWriteLock之讀寫鎖原始碼
- Golang 讀寫鎖RWMutex 互斥鎖Mutex 原始碼詳解GolangMutex原始碼
- 從自旋鎖、睡眠鎖、讀寫鎖到 Linux RCU 機制講解Linux
- StampedLock:JDK1.8中新增,比ReadWriteLock還快的鎖JDK
- Go語言中的互斥鎖和讀寫鎖(Mutex和RWMutex)GoMutex
- golang RWMutex讀寫互斥鎖原始碼分析GolangMutex原始碼
- ReentrantReadWriterLock原始碼(state設計、讀寫鎖、共享鎖、獨佔鎖及鎖降級)原始碼
- java原始碼-ReentrantReadWriteLock寫鎖介紹Java原始碼