本文是学习ReentrantLock期间记录的,按照惯例,源码翻译单独开一篇。
ReentrantLock解析https://blog.csdn.net/hhy107107/article/details/108186931
说明:在阅读ReentrantLock源码的时候顺便翻译的,大部分用的google翻译,不太通顺的改成了自己的理解。一般能看懂的,就没改。
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
/*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;
/**
* 可重入互斥{@link Lock},其基本行为和语义与使用方法和语句访问的隐式监视器锁{@code synchronized}相同,但具有扩展功能。
*
* 上一次成功锁定但尚未解锁的线程拥有{@code ReentrantLock}。 当该锁不属于另一个线程时,调用{@code lock}的线程将返回并成功获取该锁。
* 如果当前线程已经拥有该锁,则该方法将立即返回。 可以使用方法{@link #isHeldByCurrentThread}和{@link #getHoldCount}进行检查。
*
* 此类的构造函数接受一个可选的fairness参数。 设置{@code true}时,有竞争的情况下,锁倾向于授予对等待时间最长的线程的访问。
* 否则,此锁不能保证任何特定的访问顺序。 使用许多线程访问的公平锁定的程序可能会比使用默认设置的程序显示较低的总体吞吐量(即较慢;通常要慢得多),
* 但获得锁定并保证没有饥饿的时间差异较小。 但是请注意,锁的公平性不能保证线程调度的公平性。
* 因此,使用公平锁的许多线程之一可能会连续多次获得它,而其他活动线程未进行且当前未持有该锁。
* 还要注意,未定时的{@link #tryLock()}方法不支持公平性设置。 如果锁定可用,就算有其他线程正在等待,它也将成功。
*
* 建议的做法是始终立即使用{@code try}块对{@code lock}进行调用,通常是在构造之前/之后,例如:
*
* <pre> {@code
* class X {
* private final ReentrantLock lock = new ReentrantLock();
* // ...
*
* public void m() {
* lock.lock(); // block until condition holds
* try {
* // ... method body
* } finally {
* lock.unlock()
* }
* }
* }}</pre>
*
* 除了实现{@link Lock}接口之外,此类还定义了许多{@code public}和{@code protected}方法来检查锁的状态。 其中一些方法仅对仪器和监视有用。
*
* 此类的序列化与内置锁的行为相同:反序列化的锁处于解锁状态,而不管序列化时的状态如何。
*
* 该锁通过同一线程最多支持2147483647个递归锁。 尝试超过此限制会导致锁定方法引发{@link Error}。
*
* @since 1.5
* @author Doug Lea
*/
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/** 同步器提供所有实现机制 */
private final Sync sync;
/**
* 此锁的同步控制基础。 在下面细分为公平和非公平版本。 使用AQS状态表示锁的保留数。
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 执行{@link Lock#lock}。 子类化的主要原因是允许为非公平版本提供快速路径。
*/
abstract void lock();
/**
* 执行不公平的tryLock。 在子类的tryAcquire中调用。
* 非公平的获取:其实就是不管现在等待队列的情况,我先自己尝试获取下。成功了最好,不成功就入队等待
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果状态==0, 说明可以允许去获取锁
if (c == 0) {
// 通过比较交换保证设置status的一致。如果发生竞争,失败的那个返回false,
if (compareAndSetState(0, acquires)) {
// 设置获取锁的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
// 如果锁已经被获取过了,判断当前线程是不是已经获取锁的线程。这里确保了锁的可重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 如果是当前持有锁的线程又获取了次锁,state加1;state最大为2147483647(就是int的最大值,因为AQS的state是int类型)
setState(nextc);
return true;
}
return false;
}
/**
* 释放锁,
* 1. 只有当前持有锁的线程才能做释放锁操作,不然会抛IllegalMonitorStateException
* 2. 由于锁是可重入的,释放锁其实就是state - 1, 如果最终state == 0,把获取锁的线程设置为空。
* 3. 如果锁空闲,返回true, 否则返回false
*/
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
/**
* 判断当前线程是否持有锁
*/
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();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// 从外部类继承的方法
// 获取锁持有者
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
// 获取持有的数量(重入了几次)
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
// 锁是否被持有
final boolean isLocked() {
return getState() != 0;
}
/**
* 反序列化用的,可以看到,反序列化后,state = 0
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // 重置为解锁状态
}
}
/**
* 同步对象的非公平锁
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 不公平的lock, 进lock方法,啥都不判断,直接尝试通过cas修改锁状态。
* 如果修改成功,说明获取到了锁,设置当前获取锁的线程为自己,然后返回
* 如果修改失败,通过acquire方法按部就班的获取锁或入队列等待。
*
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // acquire是AQS实现的,在AQS里,会调用这里的tryAcquire
}
protected final boolean tryAcquire(int acquires) {
// 非公平的获取:其实就是不管现在等待队列的情况,我先自己尝试获取下。成功了最好,不成功就入队等待
return nonfairTryAcquire(acquires);
}
}
/**
* 同步对象的公平锁
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
// 由于AQS本身是个先入先出的队列,所以这里正常的调用acquire, 如果成功最好,如果不成功,入队。
// 由队列来保证公平
final void lock() {
acquire(1);
}
/**
* 公平的tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 注意这里,和非公平的版本相比,唯一的区别就是这里多做了一个判断 !hasQueuedPredecessors()
// hasQueuedPredecessors:{@code true} 如果当前线程之前有一个排队的线程; {@code false} 如果当前线程位于队列的开头或队列为空
// 如果有其他线程等待的时间比当前线程长,说明当前线程还没资格获取锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
/**
* 构造方法,默认为非公平,等于ReentrantLock(false)
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 构造方法,可选公平和非公平模式
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
* 获取锁(忽略中断)
*
* 如果没有其他线程持有该锁,则获取该锁并立即返回,将锁保持计数设置为1。
*
* 如果当前线程已经持有该锁,则持有计数将增加一,该方法将立即返回。
*
* 如果锁是由另一个线程持有的,则当前线程将出于线程调度目的而被禁用,并处于休眠状态,直到获取了锁为止,此时,锁持有计数被设置为1。
*/
public void lock() {
sync.lock();
}
/**
* 获取锁(会响应中断)
*
* 如果没有其他线程持有该锁,则获取该锁并立即返回,将锁保持计数设置为1。
*
* 如果当前线程已经持有该锁,则持有计数将增加一,该方法将立即返回。
*
* 如果锁是由另一个线程持有的,则当前线程将出于线程调度目的而被禁用,并处于休眠状态,直到发生以下两种情况之一:
*
* 1. 该锁由当前线程获取;
*
* 2. 其他线程{@linkplain Thread#interrupt 中断}了当前线程
*
*
* 如果当前线程获取了锁,则锁保持计数将设置为1。
*
* 如果当前线程:
*
* 1. 在进入此方法时设置了其中断状态
*
* 2. 获取锁时被{@linkplain Thread#interrupt 中断}
*
* 会抛出{@link InterruptedException}并清除当前线程的中断状态。
*
* 在此实现中,由于此方法是显式的中断点,因此优先于对中断的响应而不是正常或可重入的锁获取。
*
* @throws InterruptedException 如果当前线程被中断
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
* 仅当调用时另一个线程未持有该锁时才获取该锁。
*
* 如果没有其他线程持有该锁,则获取该锁,并立即返回值{@code true},并将锁保持计数设置为1。
* 即使已将此锁设置为使用公平的排序策略,无论是否有其他线程正在等待,对{@code tryLock()} 的调用都会立即获取该锁(如果有)。
* 即使破坏公平性,这种“barging”的行为在某些情况下还是有用的。
* 如果要遵守此锁的公平性设置,请使用几乎等效的{@link #tryLock(long, TimeUnit) tryLock(0,TimeUnit.SECONDS)}(它还会检测到中断)。
*
* 如果当前线程已经持有此锁,则持有计数将增加一,并且该方法返回{@code true}。
*
* 如果锁由另一个线程持有,则此方法将立即返回值{@code false}。
*
* @return {@code true} 锁是空闲的并由当前线程获取或该锁已被当前线程持有; {@code false} 其它情况返回false
*/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
* 如果在给定的等待时间内另一个线程未持有该锁,并且当前线程尚未{@linkplain Thread#interrupt interrupted},则获取该锁。
*
* 如果没有其他线程持有该锁,则获取该锁,并立即返回值{@code true},并将锁保持计数设置为1。
* 如果已将此锁设置为使用公平的排序策略,则如果有任何其他线程正在等待该锁,则将不会获取可用的锁.这与{@link #tryLock()}方法相反。
* 如果你想要一个允许公平插入的定时的tryLock,可以将定时和非定时形式结合在一起:
*
* <pre> {@code
* if (lock.tryLock() || lock.tryLock(timeout, unit)) {
* ...
* }}</pre>
*
* 如果当前线程已经持有此锁,则持有计数将增加一,并且该方法返回{@code true}。
*
* 如果锁是由另一个线程持有的,则出于线程调度目的,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
* 1. 该锁由当前线程获取;
*
* 2. 其他线程{@linkplain Thread#interrupt 中断}了当前线程
*
* 3. 经过指定的等待时间
*
* 如果获取了锁,则返回值{@code true},并将锁保持计数设置为1。
*
* 如果当前线程:
*
* 1. 在进入此方法时设置了其中断状态
*
* 2. 获取锁时被{@linkplain Thread#interrupt 中断}
*
* 会抛出{@link InterruptedException}并清除当前线程的中断状态。
*
* 如果经过了指定的等待时间,则返回值{@code false}。 如果时间小于或等于零,则该方法将根本不等待。
*
* 在此实现中,由于此方法是显式的中断点,因此优先于对中断的响应,而不是正常或可重入的锁定获取与指定时间流逝。
*
* @param timeout 等待锁的时间
* @param unit 单位
* @return {@code true} 该锁是空闲的,并已由当前线程获取,或者该锁已由当前线程持有; and
* {@code false} 可以获取锁之前经过了等待时间
* @throws InterruptedException 如果当前线程被中断
* @throws NullPointerException 如果时间单位为null
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
/**
* 尝试释放此锁。
*
* 如果当前线程是此锁的持有者,则保留计数将减少。 如果保持计数现在为零,则释放锁定。
* 如果当前线程不是此锁的持有者,则将引发{@link IllegalMonitorStateException}。
*
* @throws IllegalMonitorStateException 如果当前线程不持有此锁
*/
public void unlock() {
sync.release(1);
}
/**
* 返回与此{@link Lock}实例一起使用的{@link Condition}实例。
*
* 返回的{@link Condition}实例与{@link Object}监视方法({@link Object#wait()wait},{@link Object#notify notify}和{@link Object#notifyAll 当与内置监视器锁定一起使用时)。
*
* 1. 如果在调用任何{@link Condition} {@linkplain Condition#await()等待}
* 或{@linkplain Condition#signal信号通知}方法时未持有此锁,则将引发{@link IllegalMonitorStateException}。
*
* 2. 当调用condition{@linkplain Condition#await() awaiting}方法时,将释放该锁,并在它们返回之前,重新获取该锁,并将锁保持计数恢复为调用该方法时的状态。
*
* 3. 如果线程在等待过程中被{@linkplain Thread#interrupt interrupted中断},则等待将终止,将抛出{@link InterruptedException}并清除线程的中断状态。
*
* 4. 等待线程以FIFO顺序发出信号。
*
* 5. 从等待方法返回的线程的锁重新获取顺序与最初获取锁的线程的顺序相同(默认情况下未指定),但是对于fair锁,优先使用那些一直在等待锁最长的线程
*
* @return the Condition object
*/
public Condition newCondition() {
return sync.newCondition();
}
/**
* 查询当前线程对该锁的保持次数。
*
* 对于每个未与解锁动作匹配的锁定动作,线程都会拥有一个锁。
*
* 保留计数信息通常仅用于测试和调试目的。 例如,如果不应该使用已经持有的锁来输入特定的代码段,那么我们可以断言这一事实:
*
* <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
* public void m() {
* assert lock.getHoldCount() == 0;
* lock.lock();
* try {
* // ... method body
* } finally {
* lock.unlock();
* }
* }
* }}</pre>
*
* @return 当前线程对该锁的保持次数;如果当前线程未保持此锁,则为零
*/
public int getHoldCount() {
return sync.getHoldCount();
}
/**
* 查询此锁是否由当前线程持有。
*
* 与内置监视器锁定的{@link Thread#holdsLock(Object)}方法类似,此方法通常用于调试和测试。
* 例如,仅在持有锁的情况下才应调用的方法可以断言是这种情况:
*
* <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
*
* public void m() {
* assert lock.isHeldByCurrentThread();
* // ... method body
* }
* }}</pre>
*
* 它还可以用于确保以非可重入方式使用可重入锁,例如:
*
* <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
*
* public void m() {
* assert !lock.isHeldByCurrentThread();
* lock.lock();
* try {
* // ... method body
* } finally {
* lock.unlock();
* }
* }
* }}</pre>
*
* @return {@code true} 当前线程持有此锁
* {@code false} 其他情况
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
/**
* 查询此锁是否由任何线程持有。 此方法设计用于监视系统状态,而不用于同步控制。
*
* @return {@code true} 有任何线程持有此锁
* {@code false} 其他情况
*/
public boolean isLocked() {
return sync.isLocked();
}
/**
* true: 公平; false:不公平
*
* @return {@code true} 公平
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
* 返回当前拥有此锁的线程;如果不拥有,则返回{@code null}。 当非所有者的线程调用此方法时,返回值反映当前锁定状态的最大努力近似值。
* 例如,即使有线程尝试获取锁,但所有者尚未拥有所有者,所有者可能会暂时{@code null}。 设计此方法是为了便于构造提供更广泛的锁监视功能的子类。
*
* @return the owner, or {@code null} if not owned
*/
protected Thread getOwner() {
return sync.getOwner();
}
/**
* 查询是否有任何线程正在等待获取此锁。 请注意,由于取消可能随时发生,因此返回{@code true}并不能保证任何其他线程都会获得此锁。
* 此方法主要设计用于监视系统状态。
*
* @return {@code true} 可能还有其他线程在等待获取锁
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
* 查询给定线程是否正在等待获取此锁。 请注意,由于取消可能随时发生,因此返回{@code true}并不能保证该线程将获得此锁。
* 此方法主要设计用于监视系统状态。
*
* @param thread the thread
* @return {@code true} 如果给定线程已排队等待此锁
* @throws NullPointerException if the thread is null
*/
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
* 返回等待获取此锁的线程数的估计值。 该值只是一个估计值,因为在此方法遍历内部数据结构时,线程数可能会动态变化。
* 此方法设计用于监视系统状态,而不用于同步控制。
*
* @return 等待此锁的估计线程数
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
* 返回一个包含可能正在等待获取此锁的线程的集合。 因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。
* 返回的集合的元素没有特定的顺序。 设计此方法是为了便于构造子类,以提供更广泛的监视功能。
*
* @return the collection of threads
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
* 查询是否有任何线程正在等待与此锁关联的给定条件。
* 请注意,由于超时和中断可能随时发生,因此返回{@code true}并不能保证将来的{@code signal}会唤醒任何线程。
* 此方法主要设计用于监视系统状态。
*
* @param condition the condition
* @return {@code true} 是否有等待线程
* @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this lock
* @throws NullPointerException if the condition is null
*/
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);
}
/**
* 返回等待与此锁关联的给定条件的线程数的估计值。 请注意,由于超时和中断可能随时发生,因此估算值仅用作实际侍者数的上限。
* 此方法设计用于监视系统状态,而不用于同步控制。
*
* @param condition the condition
* @return 估计的等待线程数
* @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this lock
* @throws NullPointerException if the condition is null
*/
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);
}
/**
* 返回一个包含那些可能正在等待与此锁相关的给定条件的线程的集合。 因为实际的线程集在构造此结果时可能会动态变化,所以返回的集合只是尽力而为的估计。
* 返回的集合的元素没有特定的顺序。 设计此方法是为了便于构造提供更广泛的状态监视工具的子类。
*
* @param condition the condition
* @return 线程集合
* @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this lock
* @throws NullPointerException if the condition is null
*/
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);
}
/**
* 返回标识此锁定及其锁定状态的字符串。 括在括号中的状态包括字符串{@code "Unlocked"}或字符串{@code "Locked by"},
* 后跟拥有线程的{@linkplain Thread#getName名称}。
*
* @return 标识此锁及其锁状态的字符串
*/
public String toString() {
Thread o = sync.getOwner();
return super.toString() + ((o == null) ?
"[Unlocked]" :
"[Locked by thread " + o.getName() + "]");
}
}