ReentrantReadWriteLock源码解析
在这之前的好久,写过一篇关于用友如何实现并发处理的文章,文章结尾谈到了共享锁和排它锁,由于比较懒,最近几个月都没有更新博客,今天良心发现,赶紧补上。
ReentrantReadWriteLock类图
根据类图,我们首先可以清楚的看到,读写锁内部维护了一个ReadLock和一个WriteLock,他们一来Sync实现具体的功能,而Sync继承了AQS,并且提供了公平和非公平的实现。
我们知道AQS中有一个很重要的属性为state,对于不同种的Lock,表示的含义不同,比如,ReentrantLock里面,state 表示的就是锁重入的次数。
很明显,现在有ReadLock和WriteLock,我们能不能通过一个状态字段state来同时表示两种含义呢?
答案是可以,先看下Sync的抽象继承AQS:
- Sync 抽象类
/**
* Synchronization implementation for ReentrantReadWriteLock.
* Subclassed into fair and nonfair versions.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 6317671515068378041L;
/*
* Read vs write count extraction constants and functions.
* Lock state is logically divided into two unsigned shorts:
* The lower one representing the exclusive (writer) lock hold count,
* and the upper the shared (reader) hold count.
*/
static final int SHARED_SHIFT = 16;
//读锁状态单位值为65536
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
//读锁并发获得锁的线程最大值为 65535
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
//写锁的掩码,二进制,15个1
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
/** Returns the number of shared holds represented in count */
//返回某一时刻,共享锁获得的总数
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count */
//与操作,返回写锁重入的次数.
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
...
}
可以看出Sync巧妙地使用state的高16位来表示读锁的状态,也就是获取到读锁的次数,低16位来表示写锁的状态,表示锁重入的次数。解决了一个state如何表示两个单独的状态问题。
接下来看其他的属性
/**
* firstReader is the first thread to have acquired the read lock.
* firstReaderHoldCount is firstReader's hold count.
*
* <p>More precisely, firstReader is the unique thread that last
* changed the shared count from 0 to 1, and has not released the
* read lock since then; null if there is no such thread.
*
* <p>Cannot cause garbage retention unless the thread terminated
* without relinquishing its read locks, since tryReleaseShared
* sets it to null.
*
* <p>Accessed via a benign data race; relies on the memory
* model's out-of-thin-air guarantees for references.
*
* <p>This allows tracking of read holds for uncontended read
* locks to be very cheap.
*/
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
firstReader 和firstReaderHoldCount分别表示第一个获取到读锁的线程和记录第一个获取到读锁线程重入的次数。
这允许跟踪读取保持以进行无竞争获取读锁,代价很低
现在来看,第一个获取到读锁的线程和其重入的 次数都有记录,那么其他的获取到读锁的线程怎么记录的呢?
/**
* The number of reentrant read locks held by current thread.
* Initialized only in constructor and readObject.
* Removed whenever a thread's read hold count drops to 0.
*/
private transient ThreadLocalHoldCounter readHolds;
使用 readHolds 属性来记录其他读锁的重入记录
/**
* ThreadLocal subclass. Easiest to explicitly define for sake
* of deserialization mechanics.
*/
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
readHolds 是一个ThreadLocal对象,可以保证针对每个读线程都可以记录对应的重入次数。
基础的东西看完了,我们来看下具体的读写锁实现
WriteLock (写锁)
void lock()
写锁是个独占锁,也就是指在某一时刻,只能有一个线程能够获得此对象的写锁。
1)如果当前没有线程获得读/写锁,则可以直接获得锁成功,并返回;
2)如果当前对象已经有其他线程获取到了读锁或者写锁,则当前请求写锁的线程会被阻塞挂起。
3)如果本线程已经获得了当前对象的写锁,则可以重入,重入次数+1(低16位)然后返回。
- lock方法:
/**
* Acquires the write lock.
*
* <p>Acquires the write lock if neither the read nor write lock
* are held by another thread
* and returns immediately, setting the write lock hold count to
* one.
*
* <p>If the current thread already holds the write lock then the
* hold count is incremented by one and the method returns
* immediately.
*
* <p>If the lock is held by another thread then the current
* thread becomes disabled for thread scheduling purposes and
* lies dormant until the write lock has been acquired, at which
* time the write lock hold count is set to one.
*/
public void lock() {
sync.acquire(1);
}
Sync 继承的AQS
/**
* Acquires in exclusive mode, ignoring interrupts. Implemented
* by invoking at least once {@link #tryAcquire},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquire} until success. This method can be used
* to implement method {@link Lock#lock}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
AQS并没有对tryAcquire 进行实现,Sync进行了实现
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();-----(1)
int c = getState();----- (2)
int w = exclusiveCount(c);-----(3)
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)------(4)
if (w == 0 || current != getExclusiveOwnerThread())-----(5)
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)-----(6)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);-----(7)
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))-----(8)
return false;
setExclusiveOwnerThread(current);-----(9)
return true;
}
代码解释:
(1)获取当前线程
(2)获取当前的state的数值
(3)获取排他锁(写锁)的数量
(4)如果state不是0,说明有读锁或者写锁,但是排它锁(写锁)是0,则可以说明,现在有的是读锁
(5)现在是要获取写锁,但是现在已经有读锁了,则不能获得写锁。 | | 获得锁的线程不是本线程:不管其他线程获得是是读锁或者写锁,只要其他线程获得了锁,返回false。(可体现出排他性)
(6)能走到这一步的一定就是锁的重入,判断重入次数不能超过65535
(7)设置state 的值
(8)writerShouldBlock 是公平和非公平的一个处理逻辑,对应的实现为FairSync和NonfairSync,非公平的值一直为false,然后进行CAS操作进行.
最后,如果都成功了,将现在的锁的ownner设置成当前线程,获得锁成功。
void lockInterruptibly()
类似于lock()方法,不同之处在于,它会对中断进行相应,也就是其他线程调用了该线程的interrupt()方法中断了该线程时,当前先线程会抛出InterruptedException异常。
- lockInterruptibly
/**
* Acquires the write lock unless the current thread is
* {@linkplain Thread#interrupt interrupted}.
*
* <p>Acquires the write lock if neither the read nor write lock
* are held by another thread
* and returns immediately, setting the write lock hold count to
* one.
*
* <p>If the current thread already holds this lock then the
* hold count is incremented by one and the method returns
* immediately.
*
* <p>If the lock is held by another thread then the current
* thread becomes disabled for thread scheduling purposes and
* lies dormant until one of two things happens:
*
* <ul>
*
* <li>The write lock is acquired by the current thread; or
*
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
*
* </ul>
*
* <p>If the write lock is acquired by the current thread then the
* lock hold count is set to one.
*
* <p>If the current thread:
*
* <ul>
*
* <li>has its interrupted status set on entry to this method;
* or
*
* <li>is {@linkplain Thread#interrupt interrupted} while
* acquiring the write lock,
*
* </ul>
*
* then {@link InterruptedException} is thrown and the current
* thread's interrupted status is cleared.
*
* <p>In this implementation, as this method is an explicit
* interruption point, preference is given to responding to
* the interrupt over normal or reentrant acquisition of the
* lock.
*
* @throws InterruptedException if the current thread is interrupted
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
可以在AQS里面看到
/**
* Acquires in exclusive mode, aborting if interrupted.
* Implemented by first checking interrupt status, then invoking
* at least once {@link #tryAcquire}, returning on
* success. Otherwise the thread is queued, possibly repeatedly
* blocking and unblocking, invoking {@link #tryAcquire}
* until success or the thread is interrupted. This method can be
* used to implement method {@link Lock#lockInterruptibly}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
在tryAcquire之前会判断是否被中断了,自然是响应了中断的。
tryLock()
尝试获取写锁:
1)如果当前没有其他线程持有写锁或者读锁,则当前线程获取写锁会成功,然后返回true;
2)如果当前已经有其他线程持有锁,则该方法直接返回false,且当前线程不会阻塞;
3)如果当前线程已经持有了该写锁,则增加AQS状态值后(+1),返回true。
- tryLock
/**
* Acquires the write lock only if it is not held by another thread
* at the time of invocation.
*
* <p>Acquires the write lock if neither the read nor write lock
* are held by another thread
* and returns immediately with the value {@code true},
* setting the write lock hold count to one. Even when this lock has
* been set to use a fair ordering policy, a call to
* {@code tryLock()} <em>will</em> immediately acquire the
* lock if it is available, whether or not other threads are
* currently waiting for the write lock. This "barging"
* behavior can be useful in certain circumstances, even
* though it breaks fairness. If you want to honor the
* fairness setting for this lock, then use {@link
* #tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) }
* which is almost equivalent (it also detects interruption).
*
* <p>If the current thread already holds this lock then the
* hold count is incremented by one and the method returns
* {@code true}.
*
* <p>If the lock is held by another thread then this method
* will return immediately with the value {@code false}.
*
* @return {@code true} if the lock was free and was acquired
* by the current thread, or the write lock was already held
* by the current thread; and {@code false} otherwise.
*/
public boolean tryLock( ) {
return sync.tryWriteLock();
}
tryWritelock:
/**
* Performs tryLock for write, enabling barging in both modes.
* This is identical in effect to tryAcquire except for lack
* of calls to writerShouldBlock.
*/
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;
}
与tryAcquire类似,这里直接使用了NonFair的策略
boolean tryLock(long timeout,TimeUnit unit)
与tryAcquire不同,增加了时间参数,如果尝试获得锁失败后,则会把当前线程挂起指定的时间,等超时时间到了以后,当前线程被激活,如果还是么有获取到写锁,则返回false。该方法也回对中断进行相应。
- tryLock
/**
* Acquires the write lock if it is not held by another thread
* within the given waiting time and the current thread has
* not been {@linkplain Thread#interrupt interrupted}.
*
* <p>Acquires the write lock if neither the read nor write lock
* are held by another thread
* and returns immediately with the value {@code true},
* setting the write lock hold count to one. If this lock has been
* set to use a fair ordering policy then an available lock
* <em>will not</em> be acquired if any other threads are
* waiting for the write lock. This is in contrast to the {@link
* #tryLock()} method. If you want a timed {@code tryLock}
* that does permit barging on a fair lock then combine the
* timed and un-timed forms together:
*
* <pre> {@code
* if (lock.tryLock() ||
* lock.tryLock(timeout, unit)) {
* ...
* }}</pre>
*
* <p>If the current thread already holds this lock then the
* hold count is incremented by one and the method returns
* {@code true}.
*
* <p>If the lock is held by another thread then the current
* thread becomes disabled for thread scheduling purposes and
* lies dormant until one of three things happens:
*
* <ul>
*
* <li>The write lock is acquired by the current thread; or
*
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
*
* <li>The specified waiting time elapses
*
* </ul>
*
* <p>If the write lock is acquired then the value {@code true} is
* returned and the write lock hold count is set to one.
*
* <p>If the current thread:
*
* <ul>
*
* <li>has its interrupted status set on entry to this method;
* or
*
* <li>is {@linkplain Thread#interrupt interrupted} while
* acquiring the write lock,
*
* </ul>
*
* then {@link InterruptedException} is thrown and the current
* thread's interrupted status is cleared.
*
* <p>If the specified waiting time elapses then the value
* {@code false} is returned. If the time is less than or
* equal to zero, the method will not wait at all.
*
* <p>In this implementation, as this method is an explicit
* interruption point, preference is given to responding to
* the interrupt over normal or reentrant acquisition of the
* lock, and over reporting the elapse of the waiting time.
*
* @param timeout the time to wait for the write lock
* @param unit the time unit of the timeout argument
*
* @return {@code true} if the lock was free and was acquired
* by the current thread, or the write lock was already held by the
* current thread; and {@code false} if the waiting time
* elapsed before the lock could be acquired.
*
* @throws InterruptedException if the current thread is interrupted
* @throws NullPointerException if the time unit is null
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
tryAcquireNanos
/**
* Attempts to acquire in exclusive mode, aborting if interrupted,
* and failing if the given timeout elapses. Implemented by first
* checking interrupt status, then invoking at least once {@link
* #tryAcquire}, returning on success. Otherwise, the thread is
* queued, possibly repeatedly blocking and unblocking, invoking
* {@link #tryAcquire} until success or the thread is interrupted
* or the timeout elapses. This method can be used to implement
* method {@link Lock#tryLock(long, TimeUnit)}.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
* @param nanosTimeout the maximum number of nanoseconds to wait
* @return {@code true} if acquired; {@code false} if timed out
* @throws InterruptedException if the current thread is interrupted
*/
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
doAcquireNanos:
/**
* Acquires in exclusive timed mode.
*
* @param arg the acquire argument
* @param nanosTimeout max wait time
* @return {@code true} if acquired
*/
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout <= 0L)
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
通过自旋的方式一直在争取获得锁,等待超时结束。
void unlock
释放锁:
1)如果当前线程持有该锁,调用该方法会让线程对该线程持有的state进行AQS操 作(-1),如果减1后当前状态值为0,则释放该锁,否则只做减一操作;
2)如果当前线程没有持有该锁,直接调用了该方法,则会抛出IllegalMonitorStateException
/**
* Attempts to release this lock.
*
* <p>If the current thread is the holder of this lock then
* the hold count is decremented. If the hold count is now
* zero then the lock is released. If the current thread is
* not the holder of this lock then {@link
* IllegalMonitorStateException} is thrown.
*
* @throws IllegalMonitorStateException if the current thread does not
* hold this lock
*/
public void unlock() {
sync.release(1);
}
release():
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease方法AQS没有实现,RentrantReadWriteLock 里的Sync里边进行了实现。
/*
* Note that tryRelease and tryAcquire can be called by
* Conditions. So it is possible that their arguments contain
* both read and write holds that are all released during a
* condition wait and re-established in tryAcquire.
*/
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())----------(1)
throw new IllegalMonitorStateException();
int nextc = getState() - releases;--------(2)
boolean free = exclusiveCount(nextc) == 0;-----------(3)
if (free)----------(4)
setExclusiveOwnerThread(null);
setState(nextc);-----------(5)
return free;
}
isHeldExclusively():
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
源码解析:
1)isHeldExclusively判断当前线程是不是线程锁的拥有者,如果不是,则不能release ,抛出异常
2)获取现在的state数值-1
3)看一下现在的排斥锁(写锁)重入次数是不是0次
4)如果是0次,则设置当前锁的拥有者为null,返回结果
ReadLock
###void lock()
获取读锁:
1)如果当前没有其他线程持有写锁,则当前线程可以获取读锁,AQS的状态值state的高16位的指会增加1,方法返回
2)如果有其他线程持有写锁则当前线程会被阻塞
/**
* Acquires the read lock.
*
* <p>Acquires the read lock if the write lock is not held by
* another thread and returns immediately.
*
* <p>If the write lock is held by another thread then
* the current thread becomes disabled for thread scheduling
* purposes and lies dormant until the read lock has been acquired.
*/
public void lock() {
sync.acquireShared(1);
}
调用AQS的acquireShared 方法
/**
* Acquires in shared mode, ignoring interrupts. Implemented by
* first invoking at least once {@link #tryAcquireShared},
* returning on success. Otherwise the thread is queued, possibly
* repeatedly blocking and unblocking, invoking {@link
* #tryAcquireShared} until success.
*
* @param arg the acquire argument. This value is conveyed to
* {@link #tryAcquireShared} but is otherwise uninterpreted
* and can represent anything you like.
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
同样tryAcquireShared实现在Sync这个内部类类里面:
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)---------(1)
return -1;
int r = sharedCount(c);--------(2)
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {--------(3)
if (r == 0) {------------(4)
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {----------(5)
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;-----------(6)
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;---------(7)
}
return 1;
}
return fullTryAcquireShared(current);
}
源码解析:
1)如果当前线程有写锁线程,但是这个写锁的线程不是本次请求读锁的线程,则获取失败(同时告诉我们,一个线程是可以同时获取读锁和写锁的)
2)获取现在读锁的获取的次数
3)readerShouldBlock 看是否公平的处理,而且 获取到读锁的数量小于最大数量,而且执行CAS成功的话,就离获取成功更进一步了
4)如果这是第一个请求获取读锁的线程,则设置第一个获取共享锁的数据和记录重入次数
5)如果此线程等于第一个获取到读锁的线程,那么是锁的重入可以状态直接加1即可
6)非首个线程或者之前这个线程没有获得过锁,则记录下来
7)不管是首次来的还是重入获取到的,count+1对当前线程的状态,都是没有问题的,首次来的就是0+1,重入来的就是n+1(n为之前重入的次数)
如果此次不满足公平的条件,或者读锁次数达到上限则:
fullTryAcquireShared:
/**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*/
final int fullTryAcquireShared(Thread current) {
/*
* This code is in part redundant with that in
* tryAcquireShared but is simpler overall by not
* complicating tryAcquireShared with interactions between
* retries and lazily reading hold counts.
*/
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} 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;
}
}
}
进行自旋操作,一定要最终返回1或者-1
void lockInterruptibly()
- lockInterruptibly
类似于lock()方法,不同之处在于,它会对中断进行相应,也就是其他线程调用了该线程的interrupt()方法中断了该线程时,当前先线程会抛出InterruptedException异常。
/**
* Acquires the read lock unless the current thread is
* {@linkplain Thread#interrupt interrupted}.
*
* <p>Acquires the read lock if the write lock is not held
* by another thread and returns immediately.
*
* <p>If the write lock is held by another thread then the
* current thread becomes disabled for thread scheduling
* purposes and lies dormant until one of two things happens:
*
* <ul>
*
* <li>The read lock is acquired by the current thread; or
*
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
*
* </ul>
*
* <p>If the current thread:
*
* <ul>
*
* <li>has its interrupted status set on entry to this method; or
*
* <li>is {@linkplain Thread#interrupt interrupted} while
* acquiring the read lock,
*
* </ul>
*
* then {@link InterruptedException} is thrown and the current
* thread's interrupted status is cleared.
*
* <p>In this implementation, as this method is an explicit
* interruption point, preference is given to responding to
* the interrupt over normal or reentrant acquisition of the
* lock.
*
* @throws InterruptedException if the current thread is interrupted
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
我们可以看到,和写锁时一样,之前先判断一下是否已经被中断了,进行响应
/**
* Acquires in shared mode, aborting if interrupted. Implemented
* by first checking interrupt status, then invoking at least once
* {@link #tryAcquireShared}, returning on success. Otherwise the
* thread is queued, possibly repeatedly blocking and unblocking,
* invoking {@link #tryAcquireShared} until success or the thread
* is interrupted.
* @param arg the acquire argument.
* This value is conveyed to {@link #tryAcquireShared} but is
* otherwise uninterpreted and can represent anything
* you like.
* @throws InterruptedException if the current thread is interrupted
*/
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryLock()
尝试获取读锁:
1)如果当前没有其他线程持有写锁或者读锁,则当前线程获取读锁会成功,然后返回true;
2)如果当前已经有其他线程持有写锁,则该方法直接返回false,且当前线程不会阻塞;
3)如果当前线程已经持有了该读锁,则增加AQS状态值后(+1),返回true。
/**
* Acquires the read lock only if the write lock is not held by
* another thread at the time of invocation.
*
* <p>Acquires the read lock if the write lock is not held by
* another thread and returns immediately with the value
* {@code true}. Even when this lock has been set to use a
* fair ordering policy, a call to {@code tryLock()}
* <em>will</em> immediately acquire the read lock if it is
* available, whether or not other threads are currently
* waiting for the read lock. This "barging" behavior
* can be useful in certain circumstances, even though it
* breaks fairness. If you want to honor the fairness setting
* for this lock, then use {@link #tryLock(long, TimeUnit)
* tryLock(0, TimeUnit.SECONDS) } which is almost equivalent
* (it also detects interruption).
*
* <p>If the write lock is held by another thread then
* this method will return immediately with the value
* {@code false}.
*
* @return {@code true} if the read lock was acquired
*/
public boolean tryLock() {
return sync.tryReadLock();
}
Sync进行了具体的实现
/**
* Performs tryLock for read, enabling barging in both modes.
* This is identical in effect to tryAcquireShared except for
* lack of calls to readerShouldBlock.
*/
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return false;
int r = sharedCount(c);
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (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 true;
}
}
}
和写锁的套路基本一致。
boolean tryLock(long timeout,TimeUnit unit)
与tryAcquire不同,增加了时间参数,如果尝试获得锁失败后,则会把当前线程挂起指定的时间,等超时时间到了以后,当前线程被激活,如果还是么有获取到写锁,则返回false。该方法也回对中断进行相应。与写锁套路基本一致,不做赘述。
void unLock()
/**
* Attempts to release this lock.
*
* <p>If the number of readers is now zero then the lock
* is made available for write lock attempts.
*/
public void unlock() {
sync.releaseShared(1);
}
Sync指向AQS执行:
/**
* Releases in shared mode. Implemented by unblocking one or more
* threads if {@link #tryReleaseShared} returns true.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryReleaseShared} but is otherwise uninterpreted
* and can represent anything you like.
* @return the value returned from {@link #tryReleaseShared}
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
AQS指向Sync执行tryReleaseShared()
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {------------(1)
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;----------(2)
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;
}
for (;;) {-----(3)
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0;
}
}
源码解析:
1)如果需要释放锁的线程是第一个线程,那么判断是否只获得了一次,如果只有一次,那么清除首个获得读锁的记录,如果不是,也就是重入过,那么count-1
2)如果不是第一个线程获得的读锁,那么去找对应的ThreadLocal记录,如果只有一个,那么把这条记录remove掉,如果重入过,则count-1
3)不断去更新设置state,保证state是线程安全的,也就是计数是准确的然后返回
本文对RentrantReadWriteLock的读锁和写锁根据Lock接口里面的方法,针对ReadLock和WriteLock进行了源码分析,也是对之前并发文章的一个补充,让大家更能清晰的看到读写锁的实现,以及为什么叫共享锁,为什么叫排他锁。
读锁:针对多个线程请求,没有写锁的情况,多个线程都可以获取此对象的锁。
称之为共享;
写锁:只要有其他线程获取到了读锁或者写锁,那么写锁就会失败,称之为排他。
但是针对于一个线程来说先获得了写锁,是可以获得读锁的。
在看源码的思路上,作者的注释是第一手资料,为了极大程度还原,所有的注释都给与保留,毕竟如何翻译,都不如自己翻译一遍好,可能会影响观感,但是我觉得作者的注释还是保留下来更好。
参考:
【1】 Java并发编程之美 -翟陆续,薛宾田 2018年11月第一版