我的原则:先会用再说,内部慢慢来。
学以致用,根据场景学源码
文章目录
- 一、概念
- 二、ReentrantReadWriteLock 整体架构
- 三、lock.readLock().lock() (读)剖析
- 四、lock.writeLock().lock() (写)剖析
- 五、write 锁释放 lock.writeLock().unlock();
- 六、read 锁释放 lock.readLock().unlock();
- 重点讲解下:else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
- 七、场景分析
- 场景一:thread1 在 read,thread2-threadN 接着 read
- 场景二:thread1 在 read,thread1 准备来 write
- 场景三:thread1 在 read,thread2 准备来 write
- 场景四:thread1 在 read,thread1 重入 read (测试read重入)
- 场景五:thread1 在 write,thread2-N 准备来 read
- 场景六:thread1 在 write,thread2 准备来 write
- 场景七:thread1 在 write ,thread1 准备来 read (测试降级)
- 场景八:thread1 在 write, thread再次 write (测试write锁重入)
- 场景九:thread_write_01持有锁,后面1读1写3读排队(测试读写混合唤醒)
- 八、各个场景的分析结论:
- 九、结论
- 十、番外篇
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
private static final long serialVersionUID = -6992448646407690164L;
/** 读锁 */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** 写锁 */
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
/** 默认非公平锁 **/
public ReentrantReadWriteLock() {
this(false);
}
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 6317671515068378041L;
// 高16位是 read锁,低16位是 write 锁
static final int SHARED_SHIFT = 16;
// read 锁的单位, write在低16位,所以单位是1,read锁在高16位,所以单位是 1 * 2 ^ 16
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
// read 锁最大的数量
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
// write 锁最大的数量
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
// 获取当前共享锁的数量,也就是 read 锁的数量。 直接右移 16 位,剩下的就是 read 锁的数量了
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/*
*
* 获取当前独占锁的数量,也就是 write 锁的数量 ,抹掉前面16位
* 二进制运算: n & ( 2 ^ m - 1) == n mod (2 ^ m)
* 比如: 7 & ( 2 ^ 2 -1 ) = 7 mod (2 ^ 2) = 3
* 也就是取余数操作可以用与运算来解决。
*/
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
// 计数器
static final class HoldCounter {
// 某个读线程重入的次数
int count = 0;
// tid表示该线程的tid字段的值
final long tid = getThreadId(Thread.currentThread());
}
// 本地线程计数器
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
// 重写初始化方法,在没有进行set的情况下,获取的都是该HoldCounter值
public HoldCounter initialValue() {
return new HoldCounter();
}
}
private transient ThreadLocalHoldCounter readHolds;
private transient HoldCounter cachedHoldCounter;
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
Sync() {
readHolds = new ThreadLocalHoldCounter();
setState(getState()); // ensures visibility of readHolds
}
}
static final class NonfairSync extends Sync {}
static final class FairSync extends Sync {}
public static class ReadLock implements Lock, java.io.Serializable {}
public static class WriteLock implements Lock, java.io.Serializable {}
...
}
假设当前同步状态值为S,get和set的操作如下:
(1)获取写状态:
S&0x0000FFFF:将高16位全部抹去
(2)获取读状态:
S>>>16:无符号补0,右移16位
(3)写状态加1:
S+1
(4)读状态加1:
S+(1<<16)即S + 0x00010000
在代码层的判断中,如果S不等于0,那么就有两种情况:
1. write 锁被获取:S&0x0000FFFF > 0
2. read 锁被获取:S>>>16 > 0
=== 点击查看top目录 ===
独占锁 | 共享锁 |
---|---|
tryAcquire(int arg) | tryAcquireShared(int arg) |
tryAcquireNanos(int arg, long nanosTimeout) | tryAcquireSharedNanos(int arg, long nanosTimeout) |
acquire(int arg) | acquireShared(int arg) |
acquireQueued(final Node node, int arg) | doAcquireShared(int arg) |
acquireInterruptibly(int arg) | acquireSharedInterruptibly(int arg) |
doAcquireInterruptibly(int arg) | doAcquireSharedInterruptibly(int arg) |
doAcquireNanos(int arg, long nanosTimeout) | doAcquireSharedNanos(int arg, long nanosTimeout) |
release(int arg) | releaseShared(int arg) |
tryRelease(int arg) | tryReleaseShared(int arg) |
- | doReleaseShared() |
独占锁和共享锁的方法基本一一对应,加个 shared 就是共享锁的方法
public void ReentrantReadWriteLock.ReadLock#lock() {
sync.acquireShared(1);
}
public final void AbstractQueuedSynchronizer#acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
// 返回 -1 才走这里,进入队列阻塞等待
doAcquireShared(arg);
}
protected final int ReentrantReadWriteLock.Sync#tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
// 当前状态
int c = getState();
/*
画个重点:
1. 如果 write 锁线程数不是0 ,但是如果是当前thread持有锁的话,同样可以去获取读锁。这里成为锁降级。
2. 如果 write 锁线程数不是0, 但是其他 thread 来获取锁的话,直接就返回 -1了
*/
// 如果 write 的独占锁线程数不等于0,并且不是当前thread,那么就返回 -1,表示获取锁失败
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// read 锁的数量
int r = sharedCount(c);
/*
* readerShouldBlock :
* read 锁是否需要blocking。如是非公平锁 NonfairLock ,那么不用排队直接抢。
* 如果是公平锁 FairLock,那么就判断下是否线程中有没有sync队列,如果没有,那么不用排队,如果有,判断下是不是重入,是的话,不用排队。
*/
if (!readerShouldBlock() &&
// 看下是否撑爆炸了,read 锁的数量要小于最大数 MAX_COUNT
r < MAX_COUNT &&
// 设置一下 read 锁的数量
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) { // 1. read 锁数量是0
firstReader = current;
firstReaderHoldCount = 1; //第一个 reader 拿到了1个 read 锁
} else if (firstReader == current) { // 2. 第一个 reader 重入
firstReaderHoldCount++; // read 锁数量 ++
} else { // 3. read 锁数量不是0,而且 firstReader 不是当前 thread
HoldCounter rh = cachedHoldCounter; // 计数器
// 计数器为空或者计数器上的 tid不是当前thread的tid
if (rh == null || rh.tid != getThreadId(current))
// 获取当前thread的计数器
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
// 数量是 0的话,set进去
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
代码细节分析:
exclusiveCount(c),先判断 write 锁是否是0 ,不是的话,判断是否是当前线程,也不是的话,直接返回 -1
!readerShouldBlock(), 判断该线程是否需要阻塞,需要的话 ,退出走 fullTryAcquireShared。
r < MAX_COUNT, 判断下锁数量是否超出容量了,是的话,退出走 fullTryAcquireShared。
compareAndSetState(c, c + SHARED_UNIT),CAS 设置 read 锁数量。 不成功的话,退出走 fullTryAcquireShared。
(r == 0) ,上面步骤全部成功,如果 r ==0 也就是 read 锁数量是 0,那么设置第一个读线程firstReader和firstReaderHoldCount
(firstReader == current),如果 r != 0 ,并且 firstReader == current,那么意味着重入,那么就直接 ++
else,设置当前thread对应的HoldCounter的值。
假如返回 -1 之后,进入 doAcquireShared , 进行Sync队列排队,看下流程 acquireShared方法
private void AbstractQueuedSynchronizer#doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED); //注意此处,加入的是 共享锁,addWaiter 方法,看一下之前的文章
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) { //唤醒,是唤醒 同步队列的第一个
int r = tryAcquireShared(arg);
if (r >= 0) { // -1 没拿到锁,1 拿到了锁。
setHeadAndPropagate(node, r); // 这个是重点!!!这个会唤醒下一个持有共享锁的线程
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void AbstractQueuedSynchronizer#setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared(); // 这里是重点啊!!!共享锁直接进来这个方法
}
}
---
final boolean AbstractQueuedSynchronizer.Node#isShared() {
return nextWaiter == SHARED;
}
private void AbstractQueuedSynchronizer#setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
具体的入队列addWaiter ,阻塞shouldParkAfterFailedAcquire方法,看下 【线程】ReentrantLock 源码剖析 (八)
=== 点击查看top目录 ===
public void ReentrantReadWriteLock.WriteLock#lock() {
sync.acquire(1);
}
public final void AbstractQueuedSynchronizer#acquire(int arg) {
if (!tryAcquire(arg) && // 返回true的话,就是拿到lock了,无需往下走了
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // tryAcquire返回了 false,走这里进入队列
selfInterrupt();
}
protected final boolean ReentrantReadWriteLock.Sync#tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState(); // 获取状态
int w = exclusiveCount(c); // 获取 write 锁数量
if (c != 0) {
// 如果 write 锁数量不是 0 或者说目前不是重入
if (w == 0 || current != getExclusiveOwnerThread())
return false; // 获取锁失败
// 既然走到了这里,说明: w!= 0 && current == getExclusiveOwnerThread(),那么就是write数量不是0,并且是重入操作。
if (w + exclusiveCount(acquires) > MAX_COUNT) //查看是否超限
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires); // 重入,状态 +1
return true; //成功拿到锁
}
/*
* 1. writer 是否应该阻塞。 NonfairSync一直不用阻塞。 FairSync的话,就看下队列中有没有排毒跌,有的话,就阻塞
* 2. CAS 设置 state
*/
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false; // 1.阻塞 或者:2.CAS设置status不成功。返回 false
setExclusiveOwnerThread(current);
return true;
}
=== 点击查看top目录 ===
public void WriteLock#unlock() {
sync.release(1);
}
public final boolean AbstractQueuedSynchronizer#release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean ReentrantReadWriteLock.Sync#tryRelease(int releases) {
//若锁的持有者不是当前线程,抛出异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//写锁的新线程数,正常-1
int nextc = getState() - releases;
//如果独占模式重入数为0了,说明独占模式write锁被释放
boolean free = exclusiveCount(nextc) == 0;
if (free)
//若write锁的新线程数为0,则将锁的持有者设置为null
setExclusiveOwnerThread(null);
//设置写锁的新线程数
//不管独占模式是否被释放,更新独占重入数
setState(nextc);
return free;
}
=== 点击查看top目录 ===
public void ReentrantReadWriteLock.ReadLock#unlock() {
sync.releaseShared(1);
}
public final boolean AbstractQueuedSynchronizer#releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int unused) {
// 获取当前线程
Thread current = Thread.currentThread();
if (firstReader == current) { // 当前线程为第一个读线程
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1) // read 线程占用的资源数为1
firstReader = null;
else // 减少占用的资源
firstReaderHoldCount--;
} else { // 当前线程不为第一个读线程
// 获取缓存的计数器
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) // 计数器为空或者计数器的tid不为当前正在运行的线程的tid
// 获取当前线程对应的计数器
rh = readHolds.get();
// 获取计数
int count = rh.count;
if (count <= 1) { // 计数小于等于1
// 移除
readHolds.remove();
if (count <= 0) // 计数小于等于0,抛出异常
throw unmatchedUnlockException();
}
// 减少计数
--rh.count;
}
for (;;) { // 无限循环
// 获取状态
int c = getState();
// 获取状态
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc)) // 比较并进行设置
return nextc == 0;
}
}
private void AbstractQueuedSynchronizer#doReleaseShared() {
for (;;) { // 一直在自旋
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
分析一下,如何同时满足上面2个条件,紧接着走 continue ?
情形:在释放共享锁的一瞬间,有Node加入进来获取共享锁了
情形一:
看上图开始讲解:
具体的入队列addWaiter ,阻塞shouldParkAfterFailedAcquire方法,看下 【线程】ReentrantLock 源码剖析 (八)
基础: doAcquireSharedInterruptibly 内有 addWaiter 方法,addWaiter 内有 enq 方法
流程分析:
0. 在最开始的时候,就是盘古开天辟地的时候,AbstractQueuedSynchronizer#addWaiter会进入内部的enq方法,也就是图内左。好,现在有这个线程调用到了 doReleaseShared 方法。(高并非条件下,最极端的情况下,会出现我下面要讲解的情况)
1. doAcquireSharedInterruptibly 准备进入 addWaiter
2. 看左上图,一开始head是null,现在开始设置 head : compareAndSetHead(new Node()) 已经完成
3. 看右图,这时候 (h!= null && h !=tail) ,满足,进去
4. 看左上图,这时候开始设置 tail = head,一定比3先,否则3进不去
5. 看右图,ws这个时候 == 0 条件满足。准备进入 && 后面的compareAndSetWaitStatus
6. 看左下图 ,shouldParkAfterFailedAcquire 方法把 head 的waitStatus设置成了 -1,返回 false。暂时先不管后续for
7. 看右图,compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 这里waitStatus已经从0被改成 -1,所以CAS设置失败。
8. 上面条件满足,走continue。
9. 重新进入 for ,这时候 ws == Node.SIGNAL ,唤醒线程。
6.看右图,compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) CAS设置成功。
7.看左下图 ,shouldParkAfterFailedAcquire 方法把 head 的waitStatus设置成了 -1,返回 false。暂时先不管后续for
8.看右图, h == head,break 跳出,跳出这个 doReleaseShared 方法
9.看左下图 ,步骤7 继续走 shouldParkAfterFailedAcquire 返回 false。然后继续for循环,重新进入 setHeadAndPropagate 方法。
10. 重新进入 doReleaseShared 方法。这时候 ws == Node.SIGNAL ,唤醒线程。
=== 点击查看top目录 ===
代码:
public class _14_01_TestReadWriteLock {
public static void main(String[] args) throws Exception {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to lock ...");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " get the lock ... ===");
System.out.println("点击任意键唤醒线程 ...");
Scanner sc = new Scanner(System.in);
sc.nextLine();
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " ready to unlock ...");
lock.readLock().unlock();
}
}, "Read_" + i).start();
}
}
}
输出:
Read_0 wait to lock ...
Read_1 wait to lock ...
Read_1 get the lock ... ===
点击任意键唤醒线程 ...
Read_0 get the lock ... ===
点击任意键唤醒线程 ...
Read_2 wait to lock ...
Read_2 get the lock ... ===
点击任意键唤醒线程 ...
Read_3 wait to lock ...
Read_3 get the lock ... ===
点击任意键唤醒线程 ...
Read_4 wait to lock ...
Read_4 get the lock ... ===
点击任意键唤醒线程 ...
Read_3 ready to unlock ...
Read_2 ready to unlock ...
Read_1 ready to unlock ...
Read_0 ready to unlock ...
Read_4 ready to unlock ...
结论:
在第一个thread并未释放锁的时候,其他thread不阻塞,直接获取锁,往下跑。
=== 点击查看top目录 ===
看下代码:
public class _14_02_TestReadWriteLock {
public static void main(String[] args) throws Exception{
new Thread(() -> {
ReadWriteLock lock = new ReentrantReadWriteLock();
System.out.println(Thread.currentThread().getName() + " wait to read lock ...");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");
System.out.println(Thread.currentThread().getName() + " wait to lock write ...");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the lock write ...");
System.out.println(Thread.currentThread().getName() + " ready to unlock read ...");
lock.readLock().unlock();
System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
lock.writeLock().unlock();
}).start();
}
}
输出:
Thread-0 wait to read lock ...
Thread-0 get the read lock ... ===
Thread-0 wait to lock write ...
... 阻塞当中 ...
上面代码,read锁已经获得,但是write锁阻塞,read锁无法释放,上面死锁了。
注意上方的 c = getState() = 65536 ,刚刚好是 2的 16次方,因为read锁是在 高16位,所以 read 锁数量是1的话,那么就是 65536,真正的数量右移 16位。
同理,堵塞,等待 thread1 释放read 锁,释放后,可以获得锁
// 场景三:thread1 在 read,thread2 准备来 write
public class _14_03_TestReadWriteLock {
public static void main(String[] args) throws Exception{
ReadWriteLock lock = new ReentrantReadWriteLock();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to read lock ...");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");
System.out.println("点击任意键唤醒线程 ...");
Scanner sc = new Scanner(System.in);
sc.nextLine();
System.out.println(Thread.currentThread().getName() + " ready to unlock read ...");
lock.readLock().unlock();
}).start();
Thread.sleep(2000);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to lock write ...");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the lock write ...");
System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
lock.writeLock().unlock();
}).start();
}
}
}
输出:
Thread-0 wait to read lock ...
Thread-0 get the read lock ... ===
点击任意键唤醒线程 ...
Thread-1 wait to lock write ...
Thread-2 wait to lock write ...
Thread-3 wait to lock write ...
Thread-4 wait to lock write ...
Thread-5 wait to lock write ...
Thread-0 ready to unlock read ...
Thread-1 get the lock write ...
Thread-1 ready to unlock write ...
Thread-2 get the lock write ...
Thread-2 ready to unlock write ...
Thread-3 get the lock write ...
Thread-3 ready to unlock write ...
Thread-4 get the lock write ...
Thread-4 ready to unlock write ...
Thread-5 get the lock write ...
Thread-5 ready to unlock write ...
=== 点击查看top目录 ===
代码:
// 场景四:thread1 在 read,thread1 重入 read
public class _14_07_TestReadWriteLock {
public static void main(String[] args) throws Exception {
new Thread(() -> {
ReadWriteLock lock = new ReentrantReadWriteLock();
System.out.println(Thread.currentThread().getName() + " wait to read lock ...");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " get the read lock2 ... ===");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " get the read lock3 ... ===");
System.out.println(Thread.currentThread().getName() + " ready to read unlock ...");
lock.readLock().unlock();
System.out.println(Thread.currentThread().getName() + " ready to read unlock2 ...");
lock.readLock().unlock();
System.out.println(Thread.currentThread().getName() + " ready to read unlock3 ...");
lock.readLock().unlock();
}).start();
}
}
输出:
Thread-0 wait to read lock ...
Thread-0 get the read lock ... ===
Thread-0 get the read lock2 ... ===
Thread-0 get the read lock3 ... ===
Thread-0 ready to read unlock ...
Thread-0 ready to read unlock2 ...
Thread-0 ready to read unlock3 ...
代码:
public class _14_05_TestReadWriteLock {
public static void main(String[] args) throws Exception{
ReadWriteLock lock = new ReentrantReadWriteLock();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");
System.out.println("点击任意键唤醒线程 ...");
Scanner sc = new Scanner(System.in);
sc.nextLine();
System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
lock.writeLock().unlock();
}).start();
Thread.sleep(5000);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to read lock ... ");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");
System.out.println(Thread.currentThread().getName() + " ready to read unlock ...");
lock.readLock().unlock();
}).start();
}
}
}
输出:
Thread-0 wait to write lock ...
Thread-0 get the write lock ... ===
点击任意键唤醒线程 ...
Thread-1 wait to read lock ...
Thread-2 wait to read lock ...
Thread-3 wait to read lock ...
Thread-4 wait to read lock ...
Thread-5 wait to read lock ...
Thread-0 ready to unlock write ...
Thread-1 get the read lock ... ===
Thread-1 ready to read unlock ...
Thread-2 get the read lock ... ===
Thread-2 ready to read unlock ...
Thread-3 get the read lock ... ===
Thread-3 ready to read unlock ...
Thread-5 get the read lock ... ===
Thread-5 ready to read unlock ...
Thread-4 get the read lock ... ===
Thread-4 ready to read unlock ...
read共享锁解析
=== 点击查看top目录 ===
代码
public class _14_06_TestReadWriteLock {
public static void main(String[] args) throws Exception{
ReadWriteLock lock = new ReentrantReadWriteLock();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");
System.out.println("点击任意键唤醒线程 ...");
Scanner sc = new Scanner(System.in);
sc.nextLine();
System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
lock.writeLock().unlock();
}).start();
Thread.sleep(5000);
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to write lock ... ");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");
System.out.println(Thread.currentThread().getName() + " ready to write unlock ...");
lock.writeLock().unlock();
}).start();
}
}
输出:
Thread-0 wait to write lock ...
Thread-0 get the write lock ... ===
点击任意键唤醒线程 ...
Thread-1 wait to write lock ...
Thread-0 ready to unlock write ...
Thread-1 get the write lock ... ===
Thread-1 ready to write unlock ...
=== 点击查看top目录 ===
代码:
public class _14_07_TestReadWriteLock {
public static void main(String[] args) throws Exception{
new Thread(() -> {
ReadWriteLock lock = new ReentrantReadWriteLock();
System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");
System.out.println(Thread.currentThread().getName() + " wait to read lock ...");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " get the read lock ... ===");
System.out.println(Thread.currentThread().getName() + " ready to unlock read...");
lock.readLock().unlock();
System.out.println(Thread.currentThread().getName() + " ready to unlock write ...");
lock.writeLock().unlock();
}).start();
}
}
输出:
Thread-0 wait to write lock ...
Thread-0 get the write lock ... ===
Thread-0 wait to read lock ...
Thread-0 get the read lock ... ===
Thread-0 ready to unlock read...
Thread-0 ready to unlock write ...
看下图: 在 write锁被持有,并且getExclusiveOwnerThread() == current的情况下,顺利拿到 read 锁。
=== 点击查看top目录 ===
代码
// 场景八:thread1 在 write, thread再次 write (测试write锁重入)
public class _14_08_TestReadWriteLock {
public static void main(String[] args) throws Exception {
new Thread(() -> {
ReadWriteLock lock = new ReentrantReadWriteLock();
System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the write lock ... ===");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the write lock2 ... ===");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " get the write lock3 ... ===");
System.out.println(Thread.currentThread().getName() + " ready to write unlock ...");
lock.writeLock().unlock();
System.out.println(Thread.currentThread().getName() + " ready to write unlock2 ...");
lock.writeLock().unlock();
System.out.println(Thread.currentThread().getName() + " ready to write unlock3 ...");
lock.writeLock().unlock();
}).start();
}
}
输出:
Thread-0 wait to write lock ...
Thread-0 get the write lock ... ===
Thread-0 get the write lock2 ... ===
Thread-0 get the write lock3 ... ===
Thread-0 ready to write unlock ...
Thread-0 ready to write unlock2 ...
Thread-0 ready to write unlock3 ...
// 场景九:thread_write_01持有锁,后面1读1写3读排队(测试读写混合唤醒)
public class _14_09_TestReadWriteLock {
public static void main(String[] args) throws Exception {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
// 1. 上一个 write 锁
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
lock.writeLock().lock();
Scanner sc = new Scanner(System.in);
System.out.println("点击任意键唤醒线程 ...");
sc.nextLine();
System.out.println(Thread.currentThread().getName() + " ready to write unlock ...");
lock.writeLock().unlock();
},"write_01").start();
// 确保上面代码先执行
Thread.sleep(1000);
// 2. 上一个 read 锁
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " ready to write unlock ...");
lock.readLock().unlock();
},"read").start();
// 确保上面代码先执行
Thread.sleep(1000);
// 3. 上一个 write 锁
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " ready to write unlock ...");
lock.writeLock().unlock();
},"write_02").start();
// 确保上面代码先执行
Thread.sleep(1000);
// 4. 上3个 read 锁
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " wait to write lock ...");
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " ready to write unlock ...");
lock.readLock().unlock();
},"read_0" + i).start();
}
}
}
输出:
write_01 wait to write lock ...
点击任意键唤醒线程 ...
read wait to write lock ...
write_02 wait to write lock ...
read_00 wait to write lock ...
read_01 wait to write lock ...
read_02 wait to write lock ...
write_01 ready to write unlock ...
read ready to write unlock ...
write_02 ready to write unlock ...
read_01 ready to write unlock ...
read_02 ready to write unlock ...
read_00 ready to write unlock ...
同步 Sync 队列如下图:
这个场景的重点在于,read锁被唤醒的过程中,依次唤醒后面的读锁。主要是看被唤醒后的操作doAcquireShared 方法 被唤醒后走 setHeadAndPropagate 方法
所以:
=== 点击查看top目录 ===
而readwrite锁有以下三个重要的特性:
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
(2)重进入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁
=== 点击查看top目录 ===
下一章节:【线程】线程八锁与Synchronzied内部原理(十二)
上一章节:【线程】ReentrantLock 内部公平锁与非公平锁实现 (十)