读写锁

读写锁分为读锁和写锁两个部分。读的部分是共享的,可以多个线程同时使用。写锁时互斥的。同一时刻只有一个线程可以写。并且写的时候不允许读。

我们知道AQS维护了一个 int类型的同步状态变量state。那如何用一个变量来实现读写的复杂控制呢?使用按位分段保存。一个int类型变量有32位二进制组成。将这32位分为高16位和低16位,高16位保存读状态,低16位保存写状态。如下图

JAVA并发编程(12)-读写锁ReentrantReadWriteLock的实现分析_读写锁


那如何快速获取读写状态呢?通过位运算。假设当前同步状态值为S,写状态等于S&0x0000FFFF(将高16位全部抹去),读状态等于S>>>16(无符号补0右移16位)。当写状态增加1时,等于S+1,当读状态增加1时,等于S+(1<<16),也就是S+0x00010000。写锁的获取与释放

什么情况下可以获取写锁呢?

1、读写锁都没有被获取 AQS同步状态=0

2、当前线程获取了写锁(当前线程获取了读锁是不能获取写锁的,因为 这个锁不支持锁升级)

所以获取写锁代码如下:代码1 的条件过滤后就是情况2,代码2就是情况1 这两种情况尝试获取写锁。

JAVA并发编程(12)-读写锁ReentrantReadWriteLock的实现分析_java并发编程_02


写锁的释放同重入锁。因为互斥所以释放锁是单线程的。逻辑比较简单

读锁的获取与释放

读锁是共享的,那么什么时候允许获取读锁呢?
1、读写锁都没有被获取 AQS同步状态=0
2、写锁没有被获取,那么读锁是共享的 ,可以继续获取
3、写锁被当前线程获取了。当前线程还可以继续获取读锁。(获取读锁后释放写锁就是锁降级)

以上三中情况的相反的情况就是 如果一个其他线程获取了写锁则不能获取读锁。其他情况下都能获取。获取读锁代码如下:

截图中标出的代码就是排除这种情况,其他情况下都正常获取读锁。

JAVA并发编程(12)-读写锁ReentrantReadWriteLock的实现分析_实现原理_03

读锁的释放就依靠 cas更新读锁状态就行。当读锁状态=0时就是彻底释放。

锁降级

上文中我们知道一个线程在持有写锁的情况下,还可以同时持有读锁。获取读锁后如果释放写锁,就实现了锁降级。

原书中对锁降级必要性描述如下:

JAVA并发编程(12)-读写锁ReentrantReadWriteLock的实现分析_读写锁_04