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;
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head)
break;
}
}
重点讲解下:else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
看上图开始讲解:
具体的入队列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 ,唤醒线程。
- 情形二:假如,把上面的 67 步骤调换一下,会是什么场景?
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/88bc4fc89f00daec1622b8d3590fb7e2.png)
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目录 ===
七、场景分析
- 场景一: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读排队(测试读写混合唤醒)
场景一:thread1 在 read,thread2-threadN 接着 read
代码:
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目录 ===
场景二:thread1 在 read,thread1 准备来 write
看下代码:
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位。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b4c4c782375564c27f7b3d9007774152.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/565e7f3876461ad275884479cb1e3f84.png)
=== 点击查看top目录 ===
场景三:thread1 在 read,thread2 准备来 write
同理,堵塞,等待 thread1 释放read 锁,释放后,可以获得锁
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 (测试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 ...
场景五:thread1 在 write,thread2-N 准备来 read
代码:
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共享锁解析
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/bb3911b0470b1e86f48f61a8bb02a914.png)
=== 点击查看top目录 ===
场景六:thread1 在 write,thread2 准备来 write
代码
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 ...
sync 队列如下图,先排队,等待被notify
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/c4fedab76728c8791f208b844f97f39e.png)
=== 点击查看top目录 ===
场景七:thread1 在 write ,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 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();
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);
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);
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);
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 队列如下图:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5aae0adc6a6890a797746cca41ca64b1.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/5ab4fe4fcebc6fa5ae044b5ee4a4b2d3.png)
这个场景的重点在于,read锁被唤醒的过程中,依次唤醒后面的读锁。主要是看被唤醒后的操作doAcquireShared 方法 被唤醒后走 setHeadAndPropagate 方法
八、各个场景的分析结论:
- thread1 拿到read锁的时候,thread2想read可以。(共享锁可以共享)
- thread1 拿到read锁的时候,thread1想write不行。(锁无法升级)
- thread1 拿到read锁的时候,thread2想write不行。
- thread1 拿到read锁的时候,thread1想再次read可以。 (重入ok)
- thread1 拿到write锁的时候,thread2想read不行。(独占锁不允许共享)
- thread1 拿到write锁的时候,thread2 想 write不行。(独占锁不允许共享)
- thread1 拿到write锁的时候,thread1想read可以。(锁降级了)
- thread1 拿到write锁的时候,thread1想write可以。 (重入ok)
所以:
- 一个线程要想同时持有写锁和读锁,必须先获取写锁再获取读锁;
- 写锁可以“降级”为读锁;读锁不能“升级”为写锁。
=== 点击查看top目录 ===
九、结论
而readwrite锁有以下三个重要的特性:
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
(2)重进入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁
=== 点击查看top目录 ===
十、番外篇
下一章节:【线程】线程八锁与Synchronzied内部原理(十二)
上一章节:【线程】ReentrantLock 内部公平锁与非公平锁实现 (十)