ReadWriteLock 是什么
- 首先明確一下,不是說(shuō) ReentrantLock 不好,只是 ReentrantLock 某些時(shí)候有局限。
- 如果使用 ReentrantLock,可能本身是為了防止線程 A 在寫(xiě)數(shù)據(jù)、線程 B 在讀數(shù)據(jù)造成的數(shù)據(jù)不一致,
- 但這樣,如果線程 C 在讀數(shù)據(jù)、線程 D 也在讀數(shù)據(jù),讀數(shù)據(jù)是不會(huì)改變數(shù)據(jù)的,沒(méi)有必要加鎖,但是還是加鎖了,降低了程序的性能。因?yàn)檫@個(gè),才誕生了讀寫(xiě)鎖 ReadWriteLock。
- ReadWriteLock 是一個(gè)讀寫(xiě)鎖接口,讀寫(xiě)鎖是用來(lái)提升并發(fā)程序性能的鎖分離技術(shù),
- ReentrantReadWriteLock 是 ReadWriteLock 接口的一個(gè)具體實(shí)現(xiàn),實(shí)現(xiàn)了讀寫(xiě)的分離,讀鎖是共享的,寫(xiě)鎖是獨(dú)占的,讀和讀之間不會(huì)互斥,讀和寫(xiě)、寫(xiě)和讀、寫(xiě)和寫(xiě)之間才會(huì)互斥,提升了讀寫(xiě)的性能。
讀寫(xiě)鎖的意義
ReadWriteLock 接口的一個(gè)具體實(shí)現(xiàn),實(shí)現(xiàn)了讀寫(xiě)的分離,讀鎖是共享的,寫(xiě)鎖是獨(dú)占的,讀和讀之間不會(huì)互斥,讀和寫(xiě)、寫(xiě)和讀、寫(xiě)和寫(xiě)之間才會(huì)互斥,提升了讀寫(xiě)的性能。
讀寫(xiě)鎖特性: 3`
(1)公平選擇性:支持非公平(默認(rèn))和公平的鎖獲取方式,吞吐量還是非公平優(yōu)于公平。
(2)重進(jìn)入:讀鎖和寫(xiě)鎖都支持線程重進(jìn)入。
(3)鎖降級(jí):遵循獲取寫(xiě)鎖、獲取讀鎖再釋放寫(xiě)鎖的次序,寫(xiě)鎖能夠降級(jí)成為讀鎖。
使用
1、基本使用案例
package lockTest.reentrantReadWriteLock;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Author: WalkerShen
* @DATE: 2022/3/22
* @Description: 讀寫(xiě)鎖
**/
public class ReadWriteLockTest {
/**
* public ReentrantReadWriteLock(boolean fair)
* ReentrantReadWriteLock也可以實(shí)現(xiàn)公平鎖和非公平鎖
*/
private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final static Lock readLock = readWriteLock.readLock();
private final static Lock writeLock = readWriteLock.writeLock();
private final static List<Integer> dataList = new ArrayList<>();
public static void main(String[] args) {
new Thread(() -> write()).start();
new Thread(() -> write()).start();
new Thread(() -> read()).start();
new Thread(() -> read()).start();
}
/**
* 讀方法
*/
public static void read() {
readLock.lock();
for (Integer data : dataList) {
System.out.println(Thread.currentThread().getName() + "讀取數(shù)據(jù):" + data);
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
readLock.unlock();
}
}
/**
* 寫(xiě)方法
*/
public static void write() {
writeLock.lock();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "寫(xiě)數(shù)據(jù):" + i);
dataList.add(i);
}
writeLock.unlock();
}
}
結(jié)果:
Thread-0寫(xiě)數(shù)據(jù):0
Thread-0寫(xiě)數(shù)據(jù):1
Thread-0寫(xiě)數(shù)據(jù):2
Thread-0寫(xiě)數(shù)據(jù):3
Thread-0寫(xiě)數(shù)據(jù):4
Thread-1寫(xiě)數(shù)據(jù):0
Thread-1寫(xiě)數(shù)據(jù):1
Thread-1寫(xiě)數(shù)據(jù):2
Thread-1寫(xiě)數(shù)據(jù):3
Thread-1寫(xiě)數(shù)據(jù):4
Thread-2讀取數(shù)據(jù):0
Thread-2讀取數(shù)據(jù):1
Thread-2讀取數(shù)據(jù):2
Thread-3讀取數(shù)據(jù):0
Thread-2讀取數(shù)據(jù):3
Thread-3讀取數(shù)據(jù):1
Thread-2讀取數(shù)據(jù):4
Thread-3讀取數(shù)據(jù):2
Thread-2讀取數(shù)據(jù):0
Thread-3讀取數(shù)據(jù):3
Thread-2讀取數(shù)據(jù):1
Thread-3讀取數(shù)據(jù):4
Thread-2讀取數(shù)據(jù):2
Thread-3讀取數(shù)據(jù):0
Thread-2讀取數(shù)據(jù):3
Thread-3讀取數(shù)據(jù):1
Thread-2讀取數(shù)據(jù):4
Thread-3讀取數(shù)據(jù):2
Thread-3讀取數(shù)據(jù):3
Thread-3讀取數(shù)據(jù):4
從結(jié)果中可以發(fā)現(xiàn),寫(xiě)操作的時(shí)候只有Thread-0執(zhí)行完之后才到Thread-1,
而讀操作的時(shí)候,Thread-2和Thread-3是交替進(jìn)行的
2、鎖升級(jí)
在線程中,寫(xiě)鎖的級(jí)別是大于讀鎖的,意思是如果不釋放讀鎖,去獲取寫(xiě)鎖,是獲取不到的,會(huì)導(dǎo)致線程阻塞
但是如果持有寫(xiě)鎖,去獲取讀鎖,是可以獲取到的
意思就是說(shuō),讀鎖到寫(xiě)鎖,是需要釋放后才可以升級(jí)的
package lockTest.reentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @Author: WalkerShen
* @DATE: 2022/3/22
* @Description: 讀寫(xiě)鎖結(jié)合
**/
public class ReadWriteCombineTest {
private static ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();
/**
* public static class ReadLock implements Lock, java.io.Serializable
* 因?yàn)镽eadLock和WriteLock都是靜態(tài)內(nèi)部類,所以需要添加static修飾
*/
private static Lock readLock=readWriteLock.readLock();
private static Lock writeLock=readWriteLock.writeLock();
private static String data=null;
public static void main(String[] args) {
// new Thread(()->notReleaseReadLock()).start();
new Thread(()->releaseReadLock()).start();
}
/**
* 讀寫(xiě)結(jié)合
* 在寫(xiě)鎖操作的時(shí)候,沒(méi)有釋放讀鎖
* 這個(gè)時(shí)候就會(huì)阻塞
*/
public static void notReleaseReadLock(){
readLock.lock();
if(data==null){
writeLock.lock();
data="aaaa";
System.out.println(data);
writeLock.unlock();
}
readLock.unlock();
}
/**
* 讀寫(xiě)結(jié)合
* 在獲取寫(xiě)鎖之前先釋放掉讀鎖
* 輸出結(jié)果:aaaa
*/
public static void releaseReadLock(){
readLock.lock();
if(data==null){
readLock.unlock();
writeLock.lock();
data="aaaa";
System.out.println(data);
writeLock.unlock();
}
}
}