本文分析了可重入的读写锁的使用场景,基本用法,源码解析。
使用场景
首先了解下读锁与写锁的互斥性:
读锁-读锁 不互斥
读锁-写锁 互斥
写锁-写锁 互斥
也就是说在多个线程都加读锁,不存在写锁的情况下,多个线程可以并发执行下去。这样,就解决了传统上的直接加互斥锁,导致不管是读-读,还是读-写都要等待持有锁的线程释放锁。使用读写锁在合适的场景下(一般是读的次数远高于写的次数)就可以提高并发效率。
进入读锁的条件:
- 没有其他线程持有写锁
- 没有写操作,或如果有写操作那么执行写操作的线程和持有锁的线程是同一个
进入写锁的条件:
- 其他线程没有持有读锁
- 其他线程没有持有写锁
锁升级:读锁变写锁(不支持)
不支持锁升级,同一个线程不能在获取读锁的情况下再去申请写锁。
因为如果有2个线程A和B都获取了读锁,这个时候线程A又获取了写锁,线程B却还没释放读锁,那么就在不同线程间同时存在读锁和写锁,那就读锁和写锁不互斥了。
public void test() {
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readWriteLock.readLock().lock();
System.out.println("获取读锁");
readWriteLock.writeLock().lock(); // 代码阻塞在此处了
System.out.println("获取写锁");
}
锁降级:写锁变读锁(支持)
写锁可以降级成读锁,但是如果不释放掉写锁,那么后面其他线程获取任何锁都将会阻塞。
因为这个线程已经获取了写锁,那么就不可能存在其他线程获取了读锁和写锁,那在获取写锁的这段时间内你再去获取读锁也是不会和其他线程冲突的。
public void test() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(2);
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
readWriteLock.writeLock().lock();
System.out.println("获取写锁");
// 如果此处不释放锁,那么下面的多线程获取读锁就会阻塞
// readWriteLock.writeLock().unlock();
readWriteLock.readLock().lock();
System.out.println("获取读锁");
// 如果此处不释放锁,那么下面的多线程获取写锁就会阻塞(上面的写锁要释放)
// readWriteLock.readLock().unlock();
new Thread(() -> {
readWriteLock.readLock().lock();
System.out.println("另一个线程获取读锁");
countDownLatch.countDown();
// readWriteLock.readLock().unlock();
}).start();
new Thread(() -> {
// 上面所有的锁全部一一对应(哪个线程加锁,哪个线程释放锁)的释放掉后,才可以获取到写锁
readWriteLock.writeLock().lock();
System.out.println("另一个线程获取写锁");
countDownLatch.countDown();
}).start();
countDownLatch.await();
System.out.println("执行完毕");
}
基本用法
1.处理缓存
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public void processCachedData() {
// 多个线程同时访问某个对象时,先加上读锁,之后某个线程优先查看data数据是否为空
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
// 在加写锁前必须释放读锁
rwl.readLock().unlock();
// 加上写锁就可以写入数据了
rwl.writeLock().lock();
try {
// Recheck state because another thread might have,acquired write lock and changed state before we did.
// 重复检查标记,因为可能其他线程也一起进来上面等待加写锁那一步,如果这个线程执行完释放所有锁后,其他线程获取写锁进来,
// 若不判断就会重复写入数据。
if (!cacheValid) {
data = new Object(); // 模拟更新数据
cacheValid = true;
}
// 在释放写锁之前通过获取读锁降级写锁(注意此时还没有释放写锁)
// 这里是因为保证自己修改的数据自己用,如果别的线程竞争到写锁更新了数据,那自己这个线程在用数据的时候可能就是别的线程
// 更新过后的,也就是产生了脏读,所以加读锁的目的就是让其他线程没法获取到写锁,直到自己用完了数据
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // 释放写锁而此时已经持有读锁
}
}
try {
use(data); // 模拟使用数据
} finally {
// 数据用完了就可以释放掉读锁了
rwl.readLock().unlock();
}
}
2.改造HashMap为线程安全的
public class ThreadSafeMap<K, V> extends HashMap<K, V> {
//使用读写锁保证线程安全
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
private ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
private ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
@Override
public V get(Object key) {
V value;
try {
readLock.lock();
return super.get(key);
} finally {
readLock.unlock();
}
}
@Override
public V put(K key, V value) {
V ret;
try {
writeLock.lock();
return super.put(key, value);
} finally {
writeLock.unlock();
}
}
}
源码解析
1.读写锁接口:ReadWriteLock
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing
*/
Lock writeLock();
}
2.实现接口:ReentrantReadWriteLock
结构:
成员变量:
- readerLock
- writerLock
- sync
内部类:
- Sync extends AQS
- NonfairSync extends Sync
- FairSync extends Sync
- ReadLock implements Lock
- WriteLock implements Lock
方法:大多都是关于同步器Sync的方法,详见源码解析部分的说明
源码解析
第一部分:成员变量和构造器
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
private static final long serialVersionUID = -6992448646407690164L;
/** Inner class providing readlock */
// 内部类中的读锁
private final ReentrantReadWriteLock.ReadLock readerLock;
/** Inner class providing writelock */
// 内部类中的写锁
private final ReentrantReadWriteLock.WriteLock writerLock;
/** Performs all synchronization mechanics */
// 执行所有同步机制
final Sync sync;
/**
* Creates a new {@code ReentrantReadWriteLock} with
* default (nonfair) ordering properties.
* 创建ReentrantReadWriteLock,默认是非公平锁
*/
public ReentrantReadWriteLock() {
this(false);
}
/**
* Creates a new {@code ReentrantReadWriteLock} with
* the given fairness policy.
* 创建ReentrantReadWriteLock,输入同步锁策略是否公平
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
}
第二部分:内部类
先看重点:读锁和写锁
public static class ReadLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -5992448646407690164L;
private final Sync sync;
// 外部的锁对象
protected ReadLock(ReentrantReadWriteLock lock) {
// 外部的同步器Sync
sync = lock.sync;
}
// 在其他线程没有持有写锁的情况下,获取读锁并立即返回
// 如果写锁被另一个线程持有,则当前线程会阻塞,一直等待获取到读锁
public void lock() {
// 调用同步器获取读锁,详情看下面Sync中的源码分析
// 调用的就是AQS中的方法
/**
public final void acquireShared(int arg) {
// tryAcquireShared()是下面Sync实现的方法
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg); // doAcquireShared()是AQS中的方法
}
*/
sync.acquireShared(1);
}
// 判断当前线程是否中断,中断了就抛出异常,不然获取还是类似上面
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 只要没有另一个线程持有写锁,那就直接获取读锁,并立即返回true,如果另一个线程持有写锁,那就立即返回false。并且和公平非公平也无关
public boolean tryLock() {
// 实现和tryAcquireShared差不多,就是去掉了调用readerShouldBlock()
return sync.tryReadLock();
}
/**
如果在给定的等待时间内另一个线程未持有写锁定,并且当前线程尚未中断,则获取读锁。
如果写锁未由另一个线程持有,则获取读锁,并立即返回true值。 如果将此锁设置为使用公平的排序策略,那么如果有任何其他线程正在等待该锁,则将不会获取可用的锁。 这与tryLock()方法相反。 如果您想要定时的tryLock确实允许在公平锁上插入,则将定时和非定时形式组合在一起:
if (lock.tryLock() ||
lock.tryLock(timeout, unit)) {
...
}
如果写锁由另一个线程持有,则出于线程调度的目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
读锁是由当前线程获取的; 或者
其他一些线程中断当前线程。 或者
经过指定的等待时间。
如果获取了读锁,则返回值true 。
如果当前线程:
在进入此方法时已设置其中断状态; 或者
获取读锁时被中断,
然后抛出InterruptedException并清除当前线程的中断状态。
如果经过了指定的等待时间,则返回值false 。 如果时间小于或等于零,则该方法将根本不等待。
在此实现中,由于此方法是明确的中断点,因此优先于对中断的响应而不是正常或可重入的锁定获取,而是优先报告等待时间的流逝z
*/
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
// 释放锁
public void unlock() {
sync.releaseShared(1);
}
// 读锁不支持Condition
public Condition newCondition() {
throw new UnsupportedOperationException();
}
}
public static class WriteLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -4992448646407690164L;
private final Sync sync;
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
/**
获取写锁。
如果读取和写入锁定均未被另一个线程持有,则获取写入锁定并立即返回,将写入锁定保持计数设置为1。
如果当前线程已经持有写锁,则持有计数将增加一,并且该方法将立即返回。
如果锁是由另一个线程持有的,则出于线程调度的目的,当前线程将被禁用,并处于休眠状态,直到获取了写入锁为止,此时写入锁的保持计数被设置为1。
*/
public void lock() {
/**
调用的是AQS中的方法,tryAcquire()是由下面的Sync实现
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
*/
sync.acquire(1);
}
/**
除非当前线程被中断,否则获取写锁定。
如果读取和写入锁定均未被另一个线程持有,则获取写入锁定并立即返回,将写入锁定保持计数设置为1。
如果当前线程已经持有此锁,则持有计数将增加一,并且该方法将立即返回。
如果锁是由另一个线程持有的,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下两种情况之一:
写锁是由当前线程获取的; 或者
其他一些线程中断当前线程。
如果当前线程获取了写锁定,则将锁定保持计数设置为1。
如果当前线程:
在进入此方法时已设置其中断状态; 或者
获取写锁时被中断,
然后抛出InterruptedException并清除当前线程的中断状态。
在此实现中,由于此方法是显式的中断点,因此优先于对中断的响应而不是正常或可重入的锁获取。
*/
public void lockInterruptibly() throws InterruptedException {
/**
AQS的实现
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 复用了上面的tryAcquire实现
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
*/
sync.acquireInterruptibly(1);
}
// 尝试获取锁,立即返回结果,不阻塞
public boolean tryLock() {
// 实现和tryAcquire()差不多,就是去掉了调用writerShouldBlock()
return sync.tryWriteLock();
}
/**
如果在给定的等待时间内另一个线程未持有该写锁定,并且当前线程尚未中断,则获取该写锁定。
如果读取和写入锁均未由另一个线程持有,则获取写入锁,并立即返回true值,将写入锁的保持计数设置为1。 如果将此锁设置为使用公平的排序策略,则如果任何其他线程正在等待写锁,则不会获取可用锁。 这与tryLock()方法相反。 如果您想要定时的tryLock确实允许在公平锁上插入,则将定时和非定时形式组合在一起:
if (lock.tryLock() ||
lock.tryLock(timeout, unit)) {
...
}
如果当前线程已经持有此锁,则持有计数将增加一,并且该方法返回true 。
如果该锁由另一个线程持有,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
写锁是由当前线程获取的; 或者
其他一些线程中断当前线程。 或者
经过指定的等待时间
如果获取了写锁,则返回值true ,并且将写锁保持计数设置为1。
如果当前线程:
在进入此方法时已设置其中断状态; 或者
获取写锁时被中断,
然后抛出InterruptedException并清除当前线程的中断状态。
如果经过了指定的等待时间,则返回值false 。 如果时间小于或等于零,则该方法将根本不等待。
在此实现中,由于此方法是明确的中断点,因此优先于对中断的响应而不是正常或可重入的锁定获取,而是优先报告等待时间的流逝。
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// 释放锁
public void unlock() {
/**
尝试释放此锁。
如果当前线程是此锁的持有者,则保留计数将减少。 如果保持计数现在为零,则释放锁定。 如果当前线程不是此锁的持有者,则抛出IllegalMonitorStateException 。
实现是AQS中的
public final boolean release(int arg) {
// 由下面的Sync实现,判断是否是当前线程独占锁,设置锁状态
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
// 唤醒后继节点
unparkSuccessor(h);
return true;
}
return false;
}
*/
sync.release(1);
}
// 创建条件
public Condition newCondition() {
return sync.newCondition();
}
// 写锁是否由当前线程持有
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
// 当前线程对写锁的持有数
public int getHoldCount() {
return sync.getWriteHoldCount();
}
同步器,继承的是AQS
/**
* 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;
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;
/** 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; }
/**
* A counter for per-thread read hold counts.
* Maintained as a ThreadLocal; cached in cachedHoldCounter.
* 每个线程持有的读锁数量计数器,维护在ThreadLocal,缓存在cachedHoldCounter
*/
static final class HoldCounter {
int count; // initially 0
// Use id, not reference, to avoid garbage retention
// 用线程id不用引用,避免影响垃圾回收
final long tid = LockSupport.getThreadId(Thread.currentThread());
}
/**
* ThreadLocal subclass. Easiest to explicitly define for sake
* of deserialization mechanics.
*/
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
/**
* 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.
* 当前线程持有的可重入读锁的数量
* 仅在构造函数和readObject中初始化
* 当线程的读锁数降至0时将其移除。
*/
private transient ThreadLocalHoldCounter readHolds;
/**
* The hold count of the last thread to successfully acquire
* readLock. This saves ThreadLocal lookup in the common case
* where the next thread to release is the last one to
* acquire. This is non-volatile since it is just used
* as a heuristic, and would be great for threads to cache.
*
* <p>Can outlive the Thread for which it is caching the read
* hold count, but avoids garbage retention by not retaining a
* reference to the Thread.
*
* <p>Accessed via a benign data race; relies on the memory
* model's final field and out-of-thin-air guarantees.
*/
// 缓存最近一个线程获取读锁的数量
private transient HoldCounter cachedHoldCounter;
/**
* 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;
private transient int firstReaderHoldCount;
Sync() {
// 初始化读锁拥有数量
readHolds = new ThreadLocalHoldCounter();
// 确保readHolds的可见性
setState(getState()); // ensures visibility of readHolds
}
/*
* Acquires and releases use the same code for fair and
* nonfair locks, but differ in whether/how they allow barging
* when queues are non-empty.
*/
/**
* Returns true if the current thread, when trying to acquire
* the read lock, and otherwise eligible to do so, should block
* because of policy for overtaking other waiting threads.
*/
abstract boolean readerShouldBlock();
/**
* Returns true if the current thread, when trying to acquire
* the write lock, and otherwise eligible to do so, should block
* because of policy for overtaking other waiting threads.
*/
abstract boolean writerShouldBlock();
/*
* 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.
*/
@ReservedStackAccess
// 写锁
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
@ReservedStackAccess
// 尝试获取写锁
// 1.如果读取计数非0或写入计数非0并且所有者是另一个线程,则失败
// 2.如果计数饱和了,则失败
// 3.否则,如果它是可重入的获取锁或队列策略允许就拿到锁,如果是这样就更新锁的状态并把锁的持有者设置为自己
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();
// 获取锁的状态
int c = getState();
// 获取独占锁的数量
int w = exclusiveCount(c);
// 如果当前有锁
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
// 此判断的前提是有线程持有锁
// 如果w==0那么说明持有的是读锁,返回false
// 如果持有的是写锁(w!=0),但是持有写锁的线程不是当前线程,返回false
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 走到这一步说明当前线程之前就一定持有了写锁
// 如果重入的获取写锁的数量大于最大值,则抛出异常
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
// 重入的获取写锁,设置状态,返回true
setState(c + acquires);
return true;
}
// 到这里说明线程没有持有锁
// 如果获取写锁需要阻塞,或者cas的时候失败了就返回false
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 设置当前线程为锁的独占线程
setExclusiveOwnerThread(current);
return true;
}
@ReservedStackAccess
// 尝试释放锁
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
// 把firstReader往回减
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null ||
rh.tid != LockSupport.getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
// 如果当前线程不是加读锁的线程却来释放就抛出异常
if (count <= 0)
throw unmatchedUnlockException();
}
// 当前线程持有读锁的数量-1
--rh.count;
}
for (;;) {
int c = getState();
// 状态为0代表当前没有持有锁
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;
}
}
private static IllegalMonitorStateException unmatchedUnlockException() {
return new IllegalMonitorStateException(
"attempt to unlock read lock, not locked by current thread");
}
@ReservedStackAccess
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)
return -1;
// 持有共享锁的数量
int r = sharedCount(c);
// 如果读锁不需要阻塞并且共享锁的数量还没超过最大值,那就修改当前锁的状态为共享锁
// readerShouldBlock()对于公平锁和非公平锁是不一样的判断方式
// 公平锁调用AQS中的hasQueuedPredecessors():查询在队列中有没有等的比当前线程更久的线程,有就返回false。也就是队列中第一个线程不是当前线程并且队列中有等待的线程就返回true
// 非公平锁调用AQS中的apparentlyFirstQueuedIsExclusive():等待队列头结点是否是独占的?
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
// 还没有线程持有读锁
if (r == 0) {
// 把当前线程赋值给firstReader,数量赋值1
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// 如果读锁不是第一次被获取或者第一次获取读锁的线程不是当前线程
// 主要把当前线程对应的次数+1
HoldCounter rh = cachedHoldCounter;
if (rh == null ||
rh.tid != LockSupport.getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
// 如果上面获取锁失败了,那就走完整版本的获取读锁,循环获取
return fullTryAcquireShared(current);
}
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 != LockSupport.getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove(); // 移除读锁持有数量为0的线程计数器
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// CAS设置锁的状态,逻辑和上面的方法差不多
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 != LockSupport.getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
// count+1
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
/**
* Performs tryLock for write, enabling barging in both modes.
* This is identical in effect to tryAcquire except for lack
* of calls to writerShouldBlock.
*/
@ReservedStackAccess
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;
}
/**
* Performs tryLock for read, enabling barging in both modes.
* This is identical in effect to tryAcquireShared except for
* lack of calls to readerShouldBlock.
*/
@ReservedStackAccess
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 != LockSupport.getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return true;
}
}
}
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();
}
// Methods relayed to outer class
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
// Must read state before owner to ensure memory consistency
return ((exclusiveCount(getState()) == 0) ?
null :
getExclusiveOwnerThread());
}
final int getReadLockCount() {
return sharedCount(getState());
}
final boolean isWriteLocked() {
return exclusiveCount(getState()) != 0;
}
final int getWriteHoldCount() {
return isHeldExclusively() ? exclusiveCount(getState()) : 0;
}
final int getReadHoldCount() {
if (getReadLockCount() == 0)
return 0;
Thread current = Thread.currentThread();
if (firstReader == current)
return firstReaderHoldCount;
HoldCounter rh = cachedHoldCounter;
if (rh != null && rh.tid == LockSupport.getThreadId(current))
return rh.count;
int count = readHolds.get().count;
if (count == 0) readHolds.remove();
return count;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
readHolds = new ThreadLocalHoldCounter();
setState(0); // reset to unlocked state
}
final int getCount() { return getState(); }
}
公平和非公平同步器
/**
* Nonfair version of Sync
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
return false; // 非公平的获取写锁总是去竞争就行了
}
final boolean readerShouldBlock() {
/* As a heuristic to avoid indefinite writer starvation,
* block if the thread that momentarily appears to be head
* of queue, if one exists, is a waiting writer. This is
* only a probabilistic effect since a new reader will not
* block if there is a waiting writer behind other enabled
* readers that have not yet drained from the queue.
*/
return apparentlyFirstQueuedIsExclusive();
}
}
/**
* Fair version of Sync
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
// 公平的获取读写锁只需要看队列中的状态就行
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}
第三部分:方法
都是Sync中的对锁的一些检测方法,并不用于同步操作
// 获取锁是否是公平的策略
public final boolean isFair() {
return sync instanceof FairSync;
}
// 返回当前拥有写锁的线程;如果不拥有,则返回null(此方法是为了便于构造提供更广泛的锁监视功能的子类。)
protected Thread getOwner() {
return sync.getOwner();
}
// 持有读锁的数量
public int getReadLockCount() {
return sync.getReadLockCount();
}
// 写锁是否由任何线程持有
public boolean isWriteLocked() {
return sync.isWriteLocked();
}
// 写锁是否被当前线程持有
public boolean isWriteLockedByCurrentThread() {
return sync.isHeldExclusively();
}
// 当前线程对写锁的持有数量
public int getWriteHoldCount() {
return sync.getWriteHoldCount();
}
// 当前线程对该锁持有的可重入读取次数
public int getReadHoldCount() {
return sync.getReadHoldCount();
}
// 返回一个包含可能正在等待获取写锁的线程的集合。 因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。 返回的集合的元素没有特定的顺序。(此方法是为了便于构造提供更广泛的锁监视功能的子类。)
protected Collection<Thread> getQueuedWriterThreads() {
return sync.getExclusiveQueuedThreads();
}
// 返回一个包含可能正在等待获取读锁的线程的集合。 因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。 返回的集合的元素没有特定的顺序。(此方法是为了便于构造提供更广泛的锁监视功能的子类。)
protected Collection<Thread> getQueuedReaderThreads() {
return sync.getSharedQueuedThreads();
}
// 查询是否有任何线程正在等待获取读或写锁。 请注意,由于取消可能随时发生,因此返回true不能保证任何其他线程都将获得锁。
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
// 查询给定线程是否正在等待获取读取或写入锁定。 请注意,由于取消可能随时发生,因此返回true不能保证此线程将获得锁。
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
// 返回等待获取读或写锁的线程数的估计值。 该值只是一个估计值,因为在此方法遍历内部数据结构时,线程数可能会动态变化。
public final int getQueueLength() {
return sync.getQueueLength();
}
// 返回一个包含可能正在等待获取读或写锁的线程的集合。 因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。 返回的集合的元素没有特定的顺序。(此方法是为了便于构造提供更广泛的锁监视功能的子类。)
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
// 查询是否有任何线程正在等待与写锁关联的给定Condition。 请注意,因为超时和中断可能随时发生,所以true返回并不保证将来的signal会唤醒任何线程。 此方法主要设计用于监视系统状态。
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
// 返回在与写锁关联的给定Condition下等待的线程数的估计值。 请注意,由于超时和中断可能随时发生,因此估算值仅用作实际侍者数的上限。 此方法设计用于监视系统状态,而不用于同步控制。
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
// 返回一个集合,其中包含那些可能正在等待与写锁关联的给定Condition的线程。 因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。 返回的集合的元素没有特定的顺序。 设计此方法是为了便于构造提供更广泛的状态监视功能的子类。
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}