之前讲到的ReentrantLock属于排他锁,虽然可以重入多次,但同一时间只允许一个线程获取锁。而今天讲到的ReentrantReadWriteLock维护了一对锁——读锁和写锁。读锁是共享锁,允许多个线程同时持有。读锁是排他锁,同一时刻只能有一个线程持有。通过分离读锁和写锁,使得并发性相比一般的排他锁(ReentrantLock,Synchronized)有很大的性能提升,因为大多数场景中读是多于写的;读写锁还提供了如下特性支持:
1.公平性选择——公平模式下,先到的线程会先执行,非公平模式下,谁先抢到谁就先执行
2.重入——读线程在获取读锁之后,能够再次获取读锁;写线程在获取写锁后可以再次获取写锁
3.锁降级——写线程先获取写锁,在释放写锁之前获取读锁,然后再释放写锁,这时写锁就降级为了读锁
一. ReentrantReadWriteLock重要成员变量
static final int SHARED_SHIFT = 16;
// 1 00000000 00000000,一个共享锁单元
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
// 11111111 11111111
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
// 11111111 11111111
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
// 获取高16位,代表读锁数量(共享锁)
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
// 获取低16位,代表写锁数量(排它锁)
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
二.ReadLock
public static void main(String[] args) {
ReadWriteLock lock = new ReentrantReadWriteLock();
try {
lock.readLock().lock();
} finally {
lock.readLock().unlock();
}
}
获取读锁
/**
* 获取一把读锁
* ReadLock
*/
public void lock() {
sync.acquireShared(1);
}
/**
* 共享模式获取锁
* AbstractQueuedSynchronizerSource
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
// 如果获取读锁失败,就放到同步队列中去等待
doAcquireShared(arg);
}
/**
* 共享锁(读锁)获取
* Sync
*/
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
// 有其它线程已经获取了写锁,当前线程不能再获取读锁
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 共享锁(读锁)数量
int r = sharedCount(c);
/**
* 1.判断是否需要阻塞(防止写锁饥饿)
* 2.读锁数量未达到可重入的最大值限制
* 3.CAS设置共享锁数量(+1)
*/
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
// 第一次被线程获取
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// 更新首次获取读锁线程的HoldCount
firstReaderHoldCount++;
} else {
// 如果最后一次读锁是其它线程获取的
// 就把readHolds和cachedHoldCounter更新为当前线程的
HoldCounter rh = cachedHoldCounter; // 先查缓存
if (rh == null || rh.tid != getThreadId(current))
// 缓存没有命中,或缓存中关联的线程不是当前线程,就获取当前线程的HoldCounter
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
// 返回1,说明已经成功获取了一把读锁
return 1;
}
// 第一次获取读锁失败后,尝试循环获取
return fullTryAcquireShared(current);
}
/**
* 在tryAcquireShared中进行了一次快速锁获取,但是由于CAS只能允许一个线程获取锁成功,
* 且读锁是共享的,可能存在其它仍然可以获取锁的线程,所以在方法最后调用fullTryAcquireShared来进
* 行死循环的获取锁,这个方法很关键
* Sync
*/
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
// CAS自旋
for (;;) {
int c = getState();
// 再次判断独占线程
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
}
// 此处注意:如果exclusiveCount(c) != 0 && getExclusiveOwnerThread() == current
// 就会直接去循环获取锁,不会被添加进同步队列中,这是为了防止锁降级导致的死锁
else if (readerShouldBlock()) {
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();
}
}
// 此处表明,当前线程是第一次获取读锁,但同步队列中第二个节点又是写线程,为了防止写饥饿,就把该读线程放到同步队列中等待
// 如果rh.count>0,说明当前线程是读锁重入,是允许再次获取读锁的
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
// 这里CAS成功,说明成功获取一把读锁了
if (sharedCount(c) == 0) {
// 如果发现读锁数量为0,那就将当前线程设置为firstReader,firstReaderHoldCount=1
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// 如果firstReader==current,就将firstReaderHoldCount+1
firstReaderHoldCount++;
} else {
// 如果不是当前线程拥有的读锁,就将cachedHoldCounter设置为当前线程
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
}
// 返回大于0,代表获取到了读锁
return 1;
}
}
}
/**
* 判断同步队列第二个节点是否是写线程,防止写饥饿
* NonfairSync
*/
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
/**
* 判断同步队列中是否存在等待时间更长的线程
* FairSync
*/
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
/**
* 将当前线程放到阻塞队列的尾部并阻塞
* AbstractQueuedSynchronizer
*/
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
// CAS自旋
for (;;) {
final Node p = node.predecessor();
if (p == head) {
// 如果当前节点是第二个节点,就尝试竞争一次
int r = tryAcquireShared(arg);
if (r >= 0) {
// 设置头节点,并向下一个节点传播
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
/**
* 设置头节点,如果子节点也是在等待共享锁,就唤醒
* AbstractQueuedSynchronizer
*/
private void 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();
}
}
/**
* 如果头节点状态为SIGNAL,就唤醒下一个节点线程
* 如果头节点状态为0,就把状态更新为PROPAGATE
* AbstractQueuedSynchronizer
*/
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;
}
}
释放读锁
/**
* 释放读锁
* ReadLock
*/
public void unlock() {
sync.releaseShared(1);
}
/**
* 共享模式下释放锁
* AbstractQueuedSynchronizer
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
// 如果所有锁(包括读锁和写锁)都释放完了才会去唤醒下一个节点线程
doReleaseShared();
return true;
}
return false;
}
/**
* 共享模式下释放锁
* Sync
*/
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
// 如果firstReader为当前线程,就把firstReaderHoldCount减1或置空
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
// 当前线程持有的读锁数量减1或置空
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count;
}
// 所有线程持有的读锁数量减1
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// 注意,所有锁(包括读锁和写锁)都释放完了才会返回true
return nextc == 0;
}
}
三.WriteLock
public static void main(String[] args) {
ReadWriteLock lock = new ReentrantReadWriteLock();
try {
lock.writeLock().lock();
} finally {
lock.writeLock().unlock();
}
}
获取写锁
/**
* 获取写锁
* WriteLock
*/
public void lock() {
sync.acquire(1);
}
/**
* 排他模式获取锁,如果获取不到,就会放到链表尾部,线程挂起
* AbstractQueuedSynchronizer
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* 获取写锁
* Sync
*/
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// c != 0 && w == 0,说明此时有线程(包括当前线程)拥有读锁,不允许获取写锁(不支持锁升级)
// c != 0 && w != 0 && current != getExclusiveOwnerThread() 说明有其它线程有过锁降级,不允许再次获取写锁
// c != 0 && w != 0 && current == getExclusiveOwnerThread() 说明当前线程有过锁降级,允许再次获取写锁
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
}
// 写锁数量+1
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
/**
* NonfairSync
*/
final boolean writerShouldBlock() {
return false; // writers can always barge
}
/**
* 判断同步队列中是否存在等待时间更长的线程
* FairSync
*/
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
/**
* 前面已经把node节点放到了链表尾部排队了,此方法将当前线程挂起
* AbstractQueuedSynchronizer
*/
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true; // 标记是否成功拿到资源
try {
boolean interrupted = false; // 标记等待过程中是否被中断过
// CAS第二次自旋,直到获得锁
for (;;) {
final Node p = node.predecessor(); // 获取父节点
// 如果父节点是head,即当前节点是第二个节点,那么便有资格去尝试获取资源
// (可能是老大释放完资源唤醒自己,也可能被interrupt了)
if (p == head && tryAcquire(arg)) {
// 注意此处和读锁不同,这里不会唤醒下一个节点线程
setHead(node);
p.next = null; // 释放对子节点的引用,便于后面GC对子节点的回收,此时意味着head节点被移除了链表
failed = false;
return interrupted; // 返回等待过程中是否被中断过
}
if (shouldParkAfterFailedAcquire(p, node) &&
// 此处当前线程会挂起(park),等待前一个节点唤醒(unpark或interrupt)
parkAndCheckInterrupt())
// 注意此处有中断,还是会循环到上面去获取锁,其实是忽略了中断情况
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
释放写锁
/**
* 释放写锁
* WriteLock
*/
public void unlock() {
sync.release(1);
}
/**
* 在排他模式中释放锁,如果当前线程的锁全部释放,就去激活下一个节点的线程
* AbstractQueuedSynchronizer
*/
public final boolean release(int arg) {
if (tryRelease(arg)) { // 如果返回true,说明当前线程加的写锁都已经释放完了
Node h = head;
// 如果还有下一个节点,就唤醒队列中的下一个线程
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
/**
* 释放写锁
* Sync
*/
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
// 注意,写锁释放为就返回true
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}