ReentrantReadWriteLock
采用读写分离的策略,允许多个线程可以同时获取读锁。
读写锁的内部维护了一个ReadLock
和一个WriteLock
,它们依赖Sync
实现具体功能,而Sync
继承自AQS,并且也提供了公平和非公平的实现。
ReentrantReadWriteLock使用state的高16位表示读状态,也就是获取到读锁的次数;使用低16位表示获取到写锁的线程的可重入次数。
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;
//返回读锁线程数
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
//返回写锁可重入个数
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
WriteLock
public void lock() {
sync.acquire(1);
}
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
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;
}
if (writerShouldBlock() ||!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
1、如果当前AQS状态值不为0,则说明当前已经有线程获取到了读锁或写锁
2、w==0 说明状态值的低16位为0,而AQS的状态值不为0,则说明高16位不为0,已经有线程获取了读锁 return false;
3、w!=0,说明已经有线程获取了写锁,检查当前线程是不是该锁的持有者,不是: return false;
4、判断可重入次数是否超过了最大值w + exclusiveCount(acquires) > MAX_COUNT
,如果超过了,throw new Error("Maximum lock count exceeded");
否则增加当前线程的可重入次数,然后return true;
5、如果当前AQS状态值为0,则说明目前没有线程获取到读锁和写锁,会执行writerShouldBlock()
对于writerShouldBlock()
,非公平锁的实现为:
final boolean writerShouldBlock() {
return false; // writers can always barge
}
对于非公平锁来说总是返回false,然后通过compareAndSetState(c, c + acquires)
抢占式执行CAS尝试获取写锁,成功则设置当前锁的持有者位当前线程并返回true,否则返回false。
对于公平锁的实现:
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
使用hasQueuedPredecessors()
来判断
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
类似于lock()
方法,不同之处在于,它会对中断进行响应,也就是当其他线程调用了interrupt()
方法中断了当前线程时,当前线程会抛出InterruptedException
public boolean tryLock( ) {
return sync.tryWriteLock();
}
尝试获取写锁,
1、如果当前没有其他线程持有写锁或者读锁,则当前线程会获取写锁成功,return true;
2、如果当前已经有其他线程持有写锁或者读锁,return false;且当前线程并不会被阻塞。
3、如果当前线程已经持有了该写锁则简单增加AQS的状态值会直接return true;
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c != 0) {
int w = exclusiveCount(c);
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
}
if (!compareAndSetState(c, c + 1))
return false;
setExclusiveOwnerThread(current);
return true;
}
public void unlock() {
sync.release(1);
}
尝试释放锁:
1、如果当前线程持有该锁,调用该方法会让该线程持有的AQS状态值减1,减1后状态值为0则释放该锁,否则仅仅是减1而已
2、如果当前线程没有持有该锁,调用该方法会抛出IllegalMonitorStateException
ReadLock
获取读锁,
1、如果当前没有其他线程持有写锁,则当前线程可以获取读锁,AQS的状态值state的高16位的值会增加1,然后方法返回。
2、如果其他一个线程持有写锁,则当前线程会被阻塞。
public void lock() {
sync.acquireShared(1);
}
读锁的lock()方法调用了AQS的acquireShared
方法,在其内部调用了ReentrantReadWriteLock
中sync
重写的tryAcquireShared
方法。
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
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);
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;
}
return fullTryAcquireShared(current);
}
类似于lock()
方法,不同之处在于,它会对中断进行响应,也就是当其他线程调用了interrupt()
方法中断了当前线程时,当前线程会抛出InterruptedException
尝试获取写锁,
1、如果当前没有其他线程持有写锁,则当前线程会获取读锁成功,return true;
2、如果当前已经有其他线程持有写锁,return false;且当前线程并不会被阻塞。
3、如果当前线程已经持有了该读锁则简单增加AQS的状态值高16位后会直接return true;
public void unlock() {
sync.releaseShared(1);
}
具体释放锁的操作是委托给Sync来做的。
小结: