ReentrantReadWriteLock源码
1. 源码注释
在java中,synchronized内置锁和ReentrantLock都是互斥锁,一次只能有一个线程进入到被临界区。
而ReentrantReadWriteLock是一个读写锁:
- 在读取数据的时候,可以多个线程同时进入到临界区内
- 在写入数据的时候,无聊是读线程还是写线程,都是互斥的。
一般情况下,大多数都是读数据多,写数据少的情景。这种时候使用读写锁就非常合适了。
ReadWriteLock源码注释:
- 维护了一对锁
- 读锁可同时进入到临界区
- 写锁是独占的
- 读线程可以看到写线程更新过的数据 (内存同步)
- 一个写线程修改数据(互斥)
- 多个读线程并发访问共享数据
- 理论上,这种读写锁的并发性是比互斥锁要好的,但还是要看计算机的处理器和共享资源是否适合这种模式
- 一个集合要是初始化之后常常是查询数据、少量修改数据,那么是适合读写锁的
- 但是,当修改操作并发增加,那么读写锁的开销大部分都是互斥锁的了
大概说明了,在读的时候可以共享,在写的时候是互斥的。
ReentrantReadWriteLock源码注释:
- 实现了ReadWriteLock接口
- 这个类不会偏向读锁或写锁
- 默认是非公平锁,吞吐量一般比公平锁要搞
- 公平锁中,等待时间最长的写进程将获得写锁
- 当尝试公平获取读锁时,写锁释放后,读线程才能获取锁
- 可重入
- 写锁能获得读锁,读锁不能获得写锁
- 写锁可以降级成读锁,读锁不能升级为写锁。
- 读锁和写锁都支持在获取时中断
- 写锁支持条件对象,读锁不支持条件对象
总结:
- 读锁不支持条件对象,写锁支持条件对象
- 读锁不能升级为写锁,写锁可以降级为读锁
- 读写锁也有公平和非公平模式
- 读锁支持多个读线程进入临界区,写锁是互斥的
2. ReentrantReadWriteLock内部类
ReentrantReadWriteLock中比ReentrantLock多了两个内部类,来维护读锁和写锁,但是主体还是使用Syn:
- WriteLock
- ReadLock
3. 读锁和写锁的状态表示
在ReentrantLock锁上使用的是state来表示同步状态(或重入的次数),而在ReentrantReadWriteLock是这样代表读写状态的:
/**
* 将state值(int)的前16位作为读锁的state,后16位作为写锁的state
*/
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//通过位运算,拿到前16位是共享锁的数量
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//后16位是独占锁的state
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
4. 写锁
- 加锁
public void lock() {
sync.acquire(1);
}
//arg:1
public final void acquire(int arg) {
//返回true,则获取到锁,不需要执行&&后面的操作以及if成立的操作
if (!tryAcquire(arg) &&
//当tryAcquire获取不成功,则请求排队
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//acquires:1
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
//c!=0 && w ==0,则读锁数量不为0
//存在读锁时,写锁与读锁互斥
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//当加锁后锁数量达到上限,则error
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
//重入加锁(写锁)
setState(c + acquires);
return true;
}
//c==0,则当前无锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//设置当前线程持有锁
setExclusiveOwnerThread(current);
return true;
}
//公平锁
final boolean writerShouldBlock() {
//判断队列中是否有等待节点
return hasQueuedPredecessors();
}
//非公平锁
final boolean writerShouldBlock() {
//写锁一直可以插队
return false;
}
- 解锁
public void unlock() {
sync.release(1);
}
//AQS中的方法
//arg:1
public final boolean release(int arg) {
//返回true,则解锁后线程空闲
if (tryRelease(arg)) {
Node h = head;
//如果队列中有等待线程,则唤醒后继线程
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
//releases:1
protected final boolean tryRelease(int releases) {
//判断当前线程是否为持有锁的线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//nextc:解锁后的state
int nextc = getState() - releases;
//如果解锁后state为0,则锁空闲
boolean free = exclusiveCount(nextc) == 0;
if (free)
//设置持有锁的线程为null
setExclusiveOwnerThread(null);
//否则,解锁重入锁
setState(nextc);
return free;
}
5. 读锁
- 加锁
public void lock() {
sync.acquireShared(1);
}
//AQS中的方法
//arg:1
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
//unused:1
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
//当写锁不为0且当前线程不持有锁,返回-1
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//r:读锁数量
int r = sharedCount(c);
//readerShouldBlock
//在已存在读锁的情况下,就会返回false
//队列中的后继节点为写锁情况下,需要加锁
if (!readerShouldBlock() &&
//需要加锁的情况下,才判断读锁数量是否超过最大值
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
//当前线程为第一个读锁
firstReader = current;
firstReaderHoldCount = 1;
}
//读锁重入
else if (firstReader == current) {
firstReaderHoldCount++;
}
//当前线程不是第一个读锁,读锁已经被共享了
else {
HoldCounter rh = cachedHoldCounter;
//缓存为空且当前线程不是缓存线程
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
//读锁重入
rh.count++;
}
return 1;
}
//CAS失败后再次尝试获取读锁
return fullTryAcquireShared(current);
}
//再次尝试获取读锁
//每个线程各自获取读锁的次数只能选择保存在ThreadLocal中
final int fullTryAcquireShared(Tread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
if (rh == null) {
rh = cachedHoldCounter;
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)) {
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;
}
}
}
- 解锁
public void unlock() {
sync.releaseShared(1);
}
//AQS方法
//arg:1
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
//unused:1
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
if (firstReaderHoldCount == 1)
//当前无重入
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
//读锁缓存<=1,则直接remove
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
//计数器-1
--rh.count;
}
//读锁数量-1
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
//当读锁清空,没有锁时,唤醒后继线程获取锁
private void 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;
}
}
- 锁的降级
public void processData() {
readLock.lock();
if (!update) {// 如果未更新完成
readLock.unlock();// 必须先释放读锁
writeLock.lock();// 锁降级:1.获取写锁
try{
if (!update) {
// 准备数据....略
update = true;
}
readLock.lock();// 锁降级:2.获取读锁。写锁获取后这线程可以继续获取读锁,反之不行
}catch (Exception e) {
e.printStackTrace();
}finally {
writeLock.unlock(); 锁降级:3.释放写锁
}
}
try{
// 读取更新后的数据.........
}finally{
readLock.unlock();
}
}
锁的降级:
同一个线程中,获取写锁-----获取读锁-----数据操作-----释放写锁-----释放读锁
降级目的:
为了保证吞吐性,以及可见性,线程A先获取写锁,更新数据,接着需要读取更新后的数据,获取读锁,释放写锁。
从更新数据,到读取更新后数据这段时间里,由于加上了读锁,阻塞了其他的写锁操作数据,保证了数据的安全的同时增加了读锁的吞吐量。