四、lock.writeLock().lock() (写)剖析
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目录 ===
五、场景分析
- 场景一: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锁重入)
场景一: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位。
=== 点击查看top目录 ===
场景三:thread1 在 read,thread2 准备来 write
同理,堵塞,等待 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 (测试read重入)
代码:
// 场景四: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 ...
场景五: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共享锁解析
=== 点击查看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 ...
场景七: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锁重入)
代码
// 场景八: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 ...
六、各个场景的分析结论:
- 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目录 ===
七、write 锁释放 tryRelease
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目录 ===
八、read 锁释放 tryReleaseShared
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;
}
}
=== 点击查看top目录 ===
九、结论
而readwrite锁有以下三个重要的特性:
(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
(2)重进入:读锁和写锁都支持线程重进入。
(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为读锁
=== 点击查看top目录 ===
十、番外篇
下一章节:【线程】线程八锁与Synchronzied内部原理(十二)
上一章节:【线程】ReentrantLock 内部公平锁与非公平锁实现 (十)