【JUC】ReentrantReadWriteLock
ReentrantReadWriteLock
概述
先带着问题去看这个类:
著作权归https://pdai.tech所有。 链接:https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html
为什么有了ReentrantLock还需要ReentrantReadWriteLock? -- 有共享锁
ReentrantReadWriteLock底层实现原理?
ReentrantReadWriteLock底层读写状态如何设计的? 高16位为读锁,低16位为写锁
读锁和写锁的最大数量是多少? -- 16位
本地线程计数器ThreadLocalHoldCounter是用来做什么的?
答:是这里get就是相当于new了一个对象,用来存放holdCounter的localthread对象
缓存计数器HoldCounter是用来做什么的?
答:是最后一个获取到读锁的线程计数器,每当有新的线程获取到读锁,这个变量都会更新。当最后一个获取读锁的线程重复获取读锁,或者释放读锁,就会直接使用这个变量,速度更快,相当于缓存。
读锁的获取与释放是怎么实现的?
读锁实际就是计数++,释放就是计数-- ,唯一可能影响多线程的地方就是在读锁++,--之后进行状态设置的时候,有个cas操作,可能会有死循环进行操作,那么如果竞争强度很大的时候,可能一直在循环
写锁的获取与释放是怎么实现的?
写锁的可以多次上锁,但是如果是不同的线程进行上锁,那么其他线程的上锁操作就会进入AQS同步队列
写锁如果尝试上锁失败就会挂起线程
释放:
核心是就是判断是否当前执行的线程持有了锁对象(AQS),然后如果是,那么state减去相应的计数即可
RentrantReadWriteLock为什么不支持锁升级?
什么是锁的升降级? RentrantReadWriteLock为什么不支持锁升级?
先要理解什么是锁升级和降级:
著作权归https://pdai.tech所有。 链接:https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html
锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。
读锁和写锁的牵制关系是什么?
当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)
当写锁不存在的时候,读锁可以多次获取
- 重入:此锁允许reader和writer按照
ReentrantLock
的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入reader使用读取锁。
writer可以获取读取锁,但reader不能获取写入锁。 - 锁降级:重入还允许从写入锁降级为读取锁,实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。
- 锁获取的中断:读取锁和写入锁都支持锁获取期间的中断。
- Condition 支持:写入锁提供了一个
Condition
实现,对于写入锁来说,该实现的行为与ReentrantLock.newCondition()
提供的Condition
实现对ReentrantLock
所做的行为相同。当然,此Condition
只能用于写入锁。
读取锁不支持Condition
,readLock().newCondition()
会抛出UnsupportedOperationException
。 - 监测:此类支持一些确定是读取锁还是写入锁的方法。这些方法设计用于监视系统状态,而不是同步控制。
对比:
我们发现reentrantReadWriteLock和ReentrantLock类的组成上就差别有读写可重入的锁上多了2个内部类,一个叫readlock一个叫writelock类
这里读写锁的读锁是多线程共享的,即共享锁。
这里读写锁的写锁是在更改时不允许其他线程操作的,也就是排他锁。
构造函数
创建函数默认为不公平锁方式
public ReentrantReadWriteLock(boolean fair) {
// 构造函数直接初始化读锁和写锁,fair使用的是多台,默认是false
// 注意这个sync就是对aqs的实现
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
// 读锁和写锁的初始化就是把当前对象设置sync
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
内部类分析
java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock#lock
获取读锁
public void lock() {
// 这里的sync对象是非公平锁 --> 注意是reentrantreadwritelock -》 中的NonfairSync
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared
重点函数分析
读锁是如何上锁的,写锁又是如何上锁的,两者有什么差别?
firstReader的目的是什么,性能如何提升?
为什么读锁可以共享,写锁不可以?
读锁和写锁两个如何合作,会不会有冲突?
什么是锁降级?
tryAcquireShared读锁
java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquireShared
/**
* 实现父类接口,尝试获取共享锁
* 返回如果计数成功,那么就返回1
*
* @param unused
* @return
*/
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
*
* 1. 如果锁被其他线程持有,则失败
* 2. 否则,这个线程有资格lock这个wrt的状态,因此ask根据队列策略是否需要阻塞
* 如果不需要,尝试通过cas修改状态和更新count来进行授权。
* 注意,不检查是否可重入的状态哪一步, 它被推迟到完整版,以避免必须检查保留计数 更典型的非重入情况。
* 3. 如果步骤2失败,因为线程显然不合格或CAS失败或计数饱和导致的,则链到版本与完全重试循环。
*/
Thread current = Thread.currentThread();
// 获取状态
int c = getState();
// 判断写锁的计数不为0,并且当前持有线程不是自己,那么获取读锁直接失败
// exclusiveCount 获取低16位数据,如果不为0,说明有写独占线程
// sharedCount 计算写线程数
// getExclusiveOwnerThread 当前独占的线程不是自己
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 统计当前读锁个数
// sharedCount 计算写线程数
int r = sharedCount(c);
/**
* 调用当前类中的nonfairsync函数中的数据
* {@link NonfairSync#readerShouldBlock} 可以获取读锁
* 并且读锁数量没有达到上限
* compareAndSetState(c, c + SHARED_UNIT) 高位16 + 1 cas操作 --> 读锁(共享锁)个数加1
*/
if (!readerShouldBlock() &&
r < MAX_COUNT &&
// cas判断能否抢占成功
compareAndSetState(c, c + SHARED_UNIT)) {
// r 当前共享单元个数
if (r == 0) {
// 设置第一个读锁,设置第一个的目的是为了性能提升?
// ans: firstReader是获取读锁的第一个线程。如果只有一个线程获取读锁,很明显,使用这样一个变量速度更快
firstReader = current;
// 并统计当前读锁所在的线程累加的次数
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// 如果一直是当前线程进行获取读锁,那么计数就一直累加
firstReaderHoldCount++;
} else {
/**
* cachedHoldCounter 代表的是最后一个获取读锁的线程的计数器。
* 这里get就是相当于new了一个对象
* cachedHoldCounter是最后一个获取到读锁的线程计数器,每当有新的线程获取到读锁,这个变量都会更新。
* 这个变量的目的是:当最后一个获取读锁的线程重复获取读锁,或者释放读锁,就会直接使用这个变量,速度更快,相当于缓存。
* {@link ReentrantReadWriteLock.Sync.ThreadLocalHoldCounter}
* {@link HoldCounter}
*/
HoldCounter rh = cachedHoldCounter;
// 如果最后一个线程计数器是 null 或者不是当前线程,那么就新建一个 HoldCounter 对象
if (rh == null || rh.tid != getThreadId(current)) {
// 给当前线程新建一个 HoldCounter
// readHolds 实际是一个threadlocal -- 这里主要是更新了缓存
cachedHoldCounter = rh = readHolds.get();
} else if (rh.count == 0) {
// 如果不是 null,且 count 是 0,就将上个线程的 HoldCounter 覆盖本地的。
readHolds.set(rh);
}
// 对 count 加一
rh.count++;
}
/**
* add by xiaof 2022年7月4日09:10:14 如果不考虑缓存,是否可以这样写
* 后续所有用到的逻辑都要统一一下,这里主要的目的就是计数,并且前面有做cas操作
* {@link ReentrantReadWriteLock.Sync#readCount}
*/
// readCount++;
return 1;
}
// 如果cas抢占不成功/线程满了, 死循环获取读锁。包含锁降级策略。
return fullTryAcquireShared(current);
}
tryAcquire写锁获取
java.util.concurrent.locks.ReentrantReadWriteLock.Sync#tryAcquire
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
// 获取计数状态
int c = getState();
// 获取写锁数量,也就是低16位的个数
int w = exclusiveCount(c);
// 判断锁计数不为0
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
/**
* 只要状态不为0,那么在上锁的时候就会调用 acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 方法
* {@link AbstractQueuedSynchronizer#acquireQueued(java.util.concurrent.locks.AbstractQueuedSynchronizer.Node, int)}
*/
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;
}
/**
* {@link NonfairSync#writerShouldBlock()}
* 如果计数为0,第一个函数返回永远为 false
* 第二个是cas操作,吧计数++
*/
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 设置锁的持有对象为当前线程
setExclusiveOwnerThread(current);
return true;
}
fullTryAcquireShared 获取读锁失败重试
当存在2个线程竞争获取写锁,然后有一个已经获取到读锁,另外一个读锁在同步队列中的时候触发
/**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*
* reads线程满了,或者cas无法设置成功,如果可重入锁没有再tryacquire中处理的情况
*/
final int fullTryAcquireShared(Thread current) {
/**
* {@link Sync#tryAcquireShared(int)}
* 这里部分代码和tryAcquireShared中的人方法有冗余,但是整体上更简单(说白了就是一样的代码)
*/
HoldCounter rh = null;
// 这里是一个自旋重试
for (;;) {
// 获取读写锁状态数据
int c = getState();
// exclusiveCount 获取写锁个数
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
// 写锁空闲 且 公平策略决定 线程应当被阻塞
// 下面的处理是说,如果是已获取读锁的线程重入读锁时,
// 即使公平策略指示应当阻塞也不会阻塞。
// 否则,这也会导致死锁的。
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else { // 当前线程不为第一个读线程,上面那个判断
if (rh == null) {
rh = cachedHoldCounter;
// 计数器为空或者计数器的tid不为当前正在运行的线程的tid
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
// 读锁数量达到最多
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
// 申请读锁成功,下面的处理跟tryAcquireShared是类似的。
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// 设定最后一次获取读锁的缓存
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
// 缓存起来用于释放
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
实战样例
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 功能描述
*
* @since 2022-06-29
*/
public class Code01_ReadLockMAX {
public static void main(String[] args) {
// 读锁是什么,如何上锁的?
// 为什么说可以共享?如何实现共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
int count = 0;
// 最大上线 read 第65534次上锁
// 第65535次就会报错
int MAX = (1 << 16);
int BREAK = 65535;
for (int i = 0; i < MAX; i++) {
if (i == BREAK) {
System.out.println("break point");
}
System.out.println("read 第" + count++ + "次上锁");
reentrantReadWriteLock.readLock().lock();
}
System.out.println(count);
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
*
* @since 2022-06-29
*/
public class Code02_ReadLockOtherGetReadLock {
public static void main(String[] args) {
// 读锁是什么,如何上锁的?
// 为什么说可以共享?如何实现共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
AtomicInteger count = new AtomicInteger();
int MAX = (1 << 16);
int BREAK = 65535;
// 最大上线 read 第65534次上锁
// 第65535次就会报错
IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
if (count.get() == BREAK) {
System.out.println("break point");
}
reentrantReadWriteLock.readLock().lock();
}).start());
System.out.println(count.get());
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
*
* @since 2022-07-11
*/
public class Code03_ReadLockLockAndUnlock {
public static void main(String[] args) {
// 读锁是什么,如何上锁的?
// 为什么说可以共享?如何实现共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
AtomicInteger count = new AtomicInteger();
int MAX = (1 << 16);
int BREAK = 65535;
// 最大上线 read 第65534次上锁
// 第65535次就会报错
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
if (count.get() == BREAK) {
System.out.println("break point");
}
readLock.lock();
readLock.lock();
// 解锁
System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
readLock.unlock();
readLock.lock();
IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
if (count.get() == BREAK) {
System.out.println("break point");
}
readLock.lock();
// 解锁
System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
readLock.unlock();
}).start());
System.out.println(count.get());
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
*
* @since 2022-07-11
*/
public class Code04_WriteLockLockAndUnlock {
static AtomicInteger count = new AtomicInteger();
static int MAX = (1 << 16);
static int BREAK = 65535;
public static void main(String[] args) {
// 读锁是什么,如何上锁的?
// 为什么说可以共享?如何实现共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// lockStateNotZeroAndLock(reentrantReadWriteLock);
// writeLockAndUnlock(reentrantReadWriteLock);
// otherThreadWriteLock(reentrantReadWriteLock);
otherThreadWriteLockAndUnlock(reentrantReadWriteLock);
// 最大上线 read 第65534次上锁
// 第65535次就会报错
// IntStream.range(0, MAX - 1).forEach(x -> new Thread(() -> {
// System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
// if (count.get() == BREAK) {
// System.out.println("break point");
// }
// writeLock.lock();
// // 解锁
// System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
// writeLock.unlock();
// }).start());
// System.out.println(count.get());
}
public static void lockStateNotZeroAndLock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
readLock.lock();
readLock.lock();
writeLock.lock();
// 解锁
System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
writeLock.unlock();
writeLock.lock();
}
public static void writeLockAndUnlock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
writeLock.lock();
// 解锁
System.out.println(Thread.currentThread().getName() + ":read 第" + count.get() + "次解锁");
writeLock.unlock();
}
public static void otherThreadWriteLock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
writeLock.lock();
writeLock.lock();
// 另外一个线程进行上锁
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":read 第" + count.getAndIncrement() + "次上锁");
writeLock.lock();
}).start());
System.out.println(count.get());
}
public static void otherThreadWriteLockAndUnlock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// 另外一个线程进行上锁
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
while (true) {
System.out.println(Thread.currentThread().getName() + ":writeLock 第" + count.getAndIncrement() + "次上锁");
writeLock.lock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
int flag = 0;
IntStream.range(0, flag).forEach(xx -> {
System.out.println(Thread.currentThread().getName() + ":writeLock 第" + count.getAndDecrement() + "次解锁:" + xx);
writeLock.unlock();
});
}
}).start());
}
static int getChoiceWithTimeout(int timeout) {
Callable<Integer> k = () -> {
return new Scanner(System.in).nextInt();
};
Long start = System.currentTimeMillis();
int choice = 0;
boolean valid;
ExecutorService l = Executors.newFixedThreadPool(1);
Future<Integer> g;
g = l.submit(k);
done: while (System.currentTimeMillis() - start < timeout) {
do {
valid = true;
if (g.isDone()) {
try {
choice = g.get();
break done;
} catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
e.printStackTrace();
}
}
} while (!valid);
}
g.cancel(true);
return choice;
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.Scanner;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
*
* @since 2022-07-11
*/
public class Code05_WRGetLock {
static AtomicInteger count = new AtomicInteger();
static int MAX = (1 << 16);
static int BREAK = 65535;
public static void main(String[] args) {
// 读锁是什么,如何上锁的?
// 为什么说可以共享?如何实现共享
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
// wrgetlockWait(reentrantReadWriteLock);
readgetlockWait(reentrantReadWriteLock);
}
public static void wrgetlockWait(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
writeLock.lock();
// **当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)**
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
try {
System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁");
if (readLock.tryLock(4, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁" + ":success");
} else {
System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁" + ":fail");
}
} catch (Exception e) {
e.printStackTrace();
}
}).start());
getChoiceWithTimeout(1000 * 100);
}
public static void readgetlockWait(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// **当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)**
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
try {
System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁");
if (readLock.tryLock(4, TimeUnit.SECONDS)) {
System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁" + ":success");
} else {
System.out.println(Thread.currentThread() + ":在写锁存在的情况下获取读锁" + ":fail");
}
} catch (Exception e) {
e.printStackTrace();
}
}).start());
}
static int getChoiceWithTimeout(int timeout) {
Callable<Integer> k = () -> {
return new Scanner(System.in).nextInt();
};
Long start = System.currentTimeMillis();
int choice = 0;
boolean valid;
ExecutorService l = Executors.newFixedThreadPool(1);
Future<Integer> g;
g = l.submit(k);
done: while (System.currentTimeMillis() - start < timeout) {
do {
valid = true;
if (g.isDone()) {
try {
choice = g.get();
break done;
} catch (InterruptedException | ExecutionException | IllegalArgumentException e) {
e.printStackTrace();
}
}
} while (!valid);
}
g.cancel(true);
return choice;
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
* 锁降级:重入还允许从写入锁降级为读取锁,实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,**从读取锁升级到写入锁是不可能的。**
*
* @since 2022-07-11
*/
public class Code06_UpAndDownLock {
static AtomicInteger count = new AtomicInteger();
static int MAX = (1 << 16);
static int BREAK = 65535;
public static void main(String[] args) {
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
tryDownLock(reentrantReadWriteLock);
}
public static void tryDownLock(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
IntStream.range(0, 1).forEach(x -> new Thread(() -> write(readLock, writeLock)).start());
IntStream.range(0, 1).forEach(x -> new Thread(() -> read(readLock, writeLock)).start());
}
public static void read(ReentrantReadWriteLock.ReadLock readLock, ReentrantReadWriteLock.WriteLock writeLock) {
System.out.println(Thread.currentThread() + " begin get read lock");
readLock.lock();
try {
System.out.println(Thread.currentThread() + " get read lock, begin execute");
Thread.sleep(20);
System.out.println(Thread.currentThread() + " try up read lock as write lock");
// 读锁升级
writeLock.lock();
System.out.println(Thread.currentThread() + " 读锁升级为写锁成功");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
System.out.println(Thread.currentThread() + " 释放读锁");
}
}
public static void write(ReentrantReadWriteLock.ReadLock readLock, ReentrantReadWriteLock.WriteLock writeLock) {
System.out.println(Thread.currentThread() + " begin get 写 lock");
writeLock.lock();
try {
System.out.println(Thread.currentThread() + " get 写 lock, begin execute");
Thread.sleep(40);
System.out.println(Thread.currentThread() + " try up read lock as write lock");
// 写锁降级读锁
readLock.lock();
System.out.println(Thread.currentThread() + " 写锁降级为读锁成功");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread() + " 释放写锁");
writeLock.unlock();
readLock.unlock();
}
}
}
package com.juc.y2022.aqs.ReentrantReadWriteLock;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.IntStream;
/**
* 功能描述
* 获取读锁失败,重试
*
* @since 2022-07-11
*/
public class Code07_ReadLockFail {
static AtomicInteger count = new AtomicInteger();
static int MAX = (1 << 16);
static int BREAK = 65535;
public static void main(String[] args) {
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
// fulltry(reentrantReadWriteLock);
fulltry2(reentrantReadWriteLock);
}
public static void fulltry(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// **当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)**
IntStream.range(0, 2).forEach(x -> new Thread(() -> {
try {
System.out.println(Thread.currentThread() + ": 获取写锁");
writeLock.lock();
System.out.println(Thread.currentThread() + ": 获取写锁-success");
System.out.println(Thread.currentThread() + ": 获取读锁");
readLock.lock();
System.out.println(Thread.currentThread() + ": 获取读锁-success");
} catch (Exception e) {
e.printStackTrace();
}
}).start());
}
public static void fulltry2(ReentrantReadWriteLock reentrantReadWriteLock) {
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// **当写锁存在的时候,无法获取读锁(除了当前持有写锁的当前线程可以再次获取)**
IntStream.range(0, 22).forEach(x -> new Thread(() -> {
try {
System.out.println(Thread.currentThread() + ": 获取读锁");
readLock.lock();
System.out.println(Thread.currentThread() + ": 获取读锁-success");
int random1 = (int) (Math.random() * 10);
if (random1 >= 9) {
System.out.println(Thread.currentThread() + ": 获取写锁");
writeLock.lock();
System.out.println(Thread.currentThread() + ": 获取---------写锁-success");
}
} catch (Exception e) {
e.printStackTrace();
}
}).start());
}
}
总结
为什么读锁是共享锁:
根本原因是state计数的位置不同,共享锁是int的高位(前16)计数的,而且不会去判断是否已经有读锁,直接计数++即可
为什么写锁是排他锁:
state计数的是低位的16位,并且会判断是否已经存在持有写锁,如果有其他线程持有写锁,那么就会进入同步队列等待
参考
https://www.pdai.tech/md/java/thread/java-thread-x-lock-ReentrantReadWriteLock.html
https://love1024.blog.csdn.net/article/details/80235197