Lock介面、重入鎖ReentrantLock、讀寫鎖ReentrantReadWriteLock
Lock特點
Lock介面提供了和synchronized關鍵字類似的同步功能,synchronized關鍵字只是隱式低獲取鎖,而lock擁有了鎖獲取和釋放的可操作性、可中斷的獲取鎖以及超時獲取鎖等同步特性
Lock使用
Lock lock = new ReentrantLock();
lock.lock();
try{
}finally{
lock.unlock();
}
在finally塊中釋放鎖,目的是保證在獲取到鎖之後,最終能夠被釋放
不要將獲取鎖的過程中寫在try塊中,因為如果在獲取鎖(自定義鎖的實現)時候發生了異常,異常丟擲的同時,也會導致鎖無故釋放
特性 | 描述 |
1 .嘗試非阻塞地獲取鎖 | 當前執行緒嘗試獲取鎖,如果這一時刻鎖沒有被其他執行緒獲取到,則成功獲取鎖,比如synchronized如果一個執行緒獲取到鎖,另一個執行緒要獲取到鎖,就必須阻塞等待,而lock不用 |
2. 能夠被中斷的獲取鎖 | 和synchronized不同,獲取到鎖的執行緒能夠響應中斷,當獲取到lock鎖的執行緒被中斷,將丟擲異常,並且釋放鎖 |
3.支援超時獲取鎖 | lock在指定的時間去獲取鎖,如果截止時間到了,仍然無法獲取鎖,則返回 |
重入鎖ReentrantLock
能夠支援一個執行緒對資源的重複加鎖,這點synchronized也能夠做到
public class GetLockAgain {
private Lock lock1 = new ReentrantLock();
private Lock lock2 = new ReentrantLock();
public void test (){
try{
lock1.lock();
System.out.println("獲取到lock1");
lock2.lock();
System.out.println("獲取到lock2");
}finally{
lock1.unlock();
lock2.unlock();
}
}
public static void main(String[] args) {
final GetLockAgain gla = new GetLockAgain();
gla.test();
}
}
輸出:
獲取到lock1
獲取到lock2
ReentrantLock支援公平和非公平選擇
什麼是鎖的公平性?
在時間上,先請求鎖的執行緒,能夠先獲取到鎖,就是公平鎖,反之,就是非公平鎖
ReentrantLock提供了建構函式控制:
boolean fair : true ,公平鎖,false ,非公平鎖,預設的構造是非公平鎖
讀寫鎖
之前提到的鎖都是排他鎖,這些鎖在一個時刻只能被一個執行緒佔用。讀寫鎖維護了一堆鎖,讀鎖和寫鎖,讀鎖在同一個時刻
可以允許多個執行緒訪問,寫鎖只能允許一個執行緒訪問, 寫鎖是排他鎖,讀鎖是共享鎖
實際開發中,讀寫鎖的效能比排他鎖效能更好,因為很多場景下都是讀執行緒,另外可以設定讀寫鎖的公平性並且支援鎖重入
* 寫鎖的獲取
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
原始碼註釋是當前執行緒持有的讀鎖數量不為0 (也就是獲取到了讀鎖就不能在去獲取寫鎖)或者 已經持有寫鎖的執行緒不是當前執行緒,寫鎖會獲取失敗,否則能夠再次獲取
* 讀鎖的獲取
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
如果如果當前執行緒獲取到了讀鎖(CAS更新count)或者沒有獲取過,當前執行緒獲取到讀鎖,並且有如果寫鎖被其他執行緒佔用了,讀鎖會獲取失敗例如,將讀寫鎖應用在快取中
/**
* 讀寫鎖應用於快取
* @author zhouy
*
*/
public class Cache {
private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private static Lock r = lock.readLock();
private static Lock w = lock.writeLock();
private static Map<String, String> map = new HashMap<>();
/**
* 向快取中寫入,多個執行緒需要排隊執行
* @param key
* @param data
*/
public final static void put(String key,String data){
try{
w.lock();
map.put(key, data);
}finally{
w.unlock();
}
}
/**
* 讀取緩衝內容,多個執行緒可以同時訪問
* @param key
* @return
*/
public final static String read(String key){
try{
r.lock();
return map.get(key);
}finally{
r.unlock();
}
}
/**
* 清空快取
*/
public final static void clear(){
try{
w.lock();
map.clear();
}finally{
w.unlock();
}
}
}
讀寫鎖的鎖降級
鎖升級:從讀鎖升級到寫鎖
鎖降級: 從寫鎖降級到讀鎖,指的是先持有寫鎖,再去獲取讀鎖,隨後釋放之前持有的寫鎖的過程
ReentrantReadWriteLock 支援鎖降級(獲取到寫鎖,再去獲取讀鎖),不支援鎖升級(前面的原始碼可以看出,獲取到讀鎖是無法再去獲取寫鎖的)
鎖升級死鎖
public class ReadWriteLock {
private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
private Lock w = rw.writeLock();
private Lock r = rw.readLock();
public void test(){
try{
r.lock();
System.out.println("獲取到讀鎖");
w.lock();
System.out.println("獲取到寫鎖");
w.unlock();
}finally{
r.unlock();
}
}
public static void main(String[] args) {
ReadWriteLock rwl = new ReadWriteLock();
rwl.test();
}
}
輸出:
鎖降級
public class ReadWriteLock {
private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
private Lock w = rw.writeLock();
private Lock r = rw.readLock();
public void test(){
try{
w.lock();
System.out.println("獲取到寫鎖");
r.lock();
System.out.println("獲取到讀鎖");
r.unlock();
}finally{
w.unlock();
}
}
public static void main(String[] args) {
ReadWriteLock rwl = new ReadWriteLock();
rwl.test();
}
}
輸出:
為什麼使用到鎖降級?
在多執行緒環境中,如果存在一個執行緒在修改一個共享變數前需要先去讀取這個變數的值,如果是先獲取寫鎖,阻塞其他執行緒,在去讀取這個共享變數的值,就能夠保證資料的正確。
反之,如果使用讀鎖先讀取,在釋放去獲取寫鎖,更新這個值,這個值可能已經被其他執行緒改過了。
相關文章
- Lock鎖之重入鎖與讀寫鎖
- 讀寫鎖 ReentrantReadWriteLock
- 【java併發程式設計】ReentrantLock 可重入讀寫鎖Java程式設計ReentrantLock
- 讀寫鎖 ReentrantReadWriteLock 與 互斥鎖 的效率
- java併發程式設計 | 鎖詳解:AQS,Lock,ReentrantLock,ReentrantReadWriteLockJava程式設計AQSReentrantLock
- Java 重入鎖 ReentrantLock 原理分析JavaReentrantLock
- Java併發——讀寫鎖ReentrantReadWriteLockJava
- 深入理解讀寫鎖ReentrantReadWriteLock
- ReentrantReadWriteLock讀寫鎖及其在 RxCach
- 淺談Java中的鎖:Synchronized、重入鎖、讀寫鎖Javasynchronized
- 深刨顯式鎖ReentrantLock原理及其與內建鎖的區別,以及讀寫鎖ReentrantReadWriteLock使用場景ReentrantLock
- Java併發-顯式鎖篇【可重入鎖+讀寫鎖】Java
- Java 讀寫鎖 ReentrantReadWriteLock 原始碼分析Java原始碼
- 原始碼分析:ReentrantReadWriteLock之讀寫鎖原始碼
- ReentrantLock可重入鎖——原始碼詳解ReentrantLock原始碼
- 【JavaSE】Lock鎖和synchronized鎖的比較,lock鎖的特性,讀寫鎖的實現。Javasynchronized
- ReentrantLock可重入鎖、公平鎖非公平鎖區別與實現原理ReentrantLock
- AQS之ReentrantReadWriteLock寫鎖AQS
- Java併發程式設計-讀寫鎖(ReentrantReadWriteLock)Java程式設計
- ReentrantReadWriteLock讀寫鎖及其在 RxCache 中的使用
- Java併發程式設計之鎖機制之(ReentrantLock)重入鎖Java程式設計ReentrantLock
- ReentrantLock(重入鎖)功能詳解和應用演示ReentrantLock
- Java併發程式設計之鎖機制之ReentrantReadWriteLock(讀寫鎖)Java程式設計
- 可重入鎖與不可重入鎖理解
- 自旋鎖、阻塞鎖、可重入鎖、悲觀鎖、樂觀鎖、讀寫鎖、偏向所、輕量級鎖、重量級鎖、鎖膨脹、物件鎖和類鎖物件
- Java併發基礎-鎖的使用及原理(可重入鎖、讀寫鎖、內建鎖、訊號量等)Java
- java高併發系列 - 第12天JUC:ReentrantLock重入鎖JavaReentrantLock
- 【Java】深入理解ReentrantLock可重入鎖之簡單使用JavaReentrantLock
- 關於 ReentrantLock 中鎖 lock() 和解鎖 unlock() 的底層原理淺析ReentrantLock
- java原始碼-ReentrantReadWriteLock寫鎖介紹Java原始碼
- Java併發指南10:Java 讀寫鎖 ReentrantReadWriteLock 原始碼分析Java原始碼
- 死鎖和可重入鎖
- 可重入鎖
- java重入鎖、公平鎖和非公平鎖Java
- redis分散式鎖-可重入鎖Redis分散式
- Java中的讀寫鎖ReentrantReadWriteLock詳解,存在一個小缺陷Java
- Java併發包原始碼學習系列:ReentrantReadWriteLock讀寫鎖解析Java原始碼
- Java鎖之ReentrantReadWriteLockJava