/*
* 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/
*/
前面的文章中,我们看了CAS原理和AQS机制,今天我们来看已下可重入锁ReentrantLock。
ReentrantLock本质上一种独占锁,获取锁方式有公平与非公平获取锁方式。
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
/**
* A reentrant mutual exclusion {@link Lock} with the same basic
* behavior and semantics as the implicit monitor lock accessed using
* {@code synchronized} methods and statements, but with extended
* capabilities.
*
一个与implicit monitor lock具有相同功能的可扩展的可重入互质锁。
* A {@code ReentrantLock} is [i]owned[/i] by the thread last
* successfully locking, but not yet unlocking it. A thread invoking
* {@code lock} will return, successfully acquiring the lock, when
* the lock is not owned by another thread. The method will return
* immediately if the current thread already owns the lock. This can
* be checked using methods {@link #isHeldByCurrentThread}, and {@link
* #getHoldCount}.
可重入锁,被上次成功获取锁,还没释放的线程,所拥有的;当锁没有被其他线程所
持有,线程可以调用lock函数,获取锁;当锁的持有者为当前线程,当前线程调用lock函数,
立刻返回,并获取锁;可用isHeldByCurrentThread方法,判断锁是否被当前单线程所持有,
用getHoldCount获取当前线程,持有锁的次数(在线程持有锁,再次调用lock,成功获取锁的次数)。
*
*
The constructor for this class accepts an optional
* [i]fairness[/i] parameter. When set {@code true}, under
* contention, locks favor granting access to the longest-waiting
* thread. Otherwise this lock does not guarantee any particular
* access order. Programs using fair locks accessed by many threads
* may display lower overall throughput (i.e., are slower; often much
* slower) than those using the default setting, but have smaller
* variances in times to obtain locks and guarantee lack of
* starvation. Note however, that fairness of locks does not guarantee
* fairness of thread scheduling. Thus, one of many threads using a
* fair lock may obtain it multiple times in succession while other
* active threads are not progressing and not currently holding the
* lock.
* Also note that the untimed {@link #tryLock() tryLock} method does not
* honor the fairness setting. It will succeed if the lock
* is available even if other threads are waiting.
*
ReentrantLock的构造函数有一个公平性参数boolean,来确定,可重入锁是公平锁,还是非公平锁。
如果是公平锁,当锁没有持有者时,将锁授予,最早等待获取锁的线程;非公平锁,不能保证按照
获取锁的顺序,将锁的授予线程;非公平锁在性能上,更优一些,但在获取锁的尝试次数和保证
lack of starvation(锁的饥渴性,暂时这么翻译)上,两种锁没有太多的差别。公平锁,也不能
绝对的保证公平性,比如,当其他的线程等待锁的时候,一个线程持有锁,也许在持有锁的过程中,
多次获取锁。tryLock也不能保证公平性,即使其他的线程在等待锁,一个线程持有锁,调用tryLock
如果锁可利用,则线程获取锁成功。
为什么,非公平锁的性能比公平锁要高呢?假设现在有一些线程在等待锁,当锁被持有者释放时,
这时,正好有一个线程获取锁,非公平锁则获取成功,公平锁则要从锁等待队列线程中,唤醒一个线程
,进入就绪运行状态,切换上下文,倒不如,让正在请求锁的线程,直接获取锁。
*
It is recommended practice to [i]always[/i] immediately
* follow a call to {@code lock} with a {@code try} block, most
* typically in a before/after construction such as:
*
在使用ReentrantLock时候,强烈建议在获取锁后面使用try语句块,以便在
finally中释放锁,如下
*
* class X {
* private final ReentrantLock lock = new ReentrantLock();
* // ...
*
* public void m() {
* lock.lock(); // block until condition holds
* try {
* // ... method body
* } finally {
* lock.unlock()
* }
* }
* }
*
*
* In addition to implementing the {@link Lock} interface, this
* class defines methods {@code isLocked} and
* {@code getLockQueueLength}, as well as some associated
* {@code protected} access methods that may be useful for
* instrumentation and monitoring.
对于实现可重入锁ReentrantLock,除了Lock中的方法外,还可以调用
ReentrantLock锁的isLocked和getLockQueueLength方法,和一些protected的
方法,以便监视锁的状态
*
*
Serialization of this class behaves in the same way as built-in
* locks: a deserialized lock is in the unlocked state, regardless of
* its state when serialized.
*
序列化可重入锁ReentrantLock,则忽略锁状态,反序列化时,锁处于unlocked state;
*
This lock supports a maximum of 2147483647 recursive locks by
* the same thread. Attempts to exceed this limit result in
* {@link Error} throws from locking methods.
*一个线程可以持有锁的次数为2147483647(2^31-1),当尝试次数,超过最大限制时,则
抛出异常,如果线程在持有2147483647次的情况下,再TryAcquire,则锁的持有数为-1;
超过整数范围溢出;
* @since 1.5
* @author Doug Lea
*/
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
//同步器,提供所有锁机制
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
//可重入锁,依赖于同步Sync,同步是基于AQS的实现;同步Sync
//有两种实现一种是公平锁,一种是非公平锁;用AQS state表示,锁的状态。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
在非公平锁实现中,允许快速获取锁
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
//在非公平锁的尝试获取锁方法中,会调用nonfairTryAcquire
//acquires为尝试获取次数,一般为1
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
int c = getState();//获取锁状态
if (c == 0) {//如果没有线程持有锁
if (compareAndSetState(0, acquires)) {
//尝试获取锁,如果获取成功,则设置锁的持有者,为当前线程,返回ture
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
//如果锁被线程持有,判断持有者是不是当前线程;
//如果当前线程是锁的持有者,则锁被当前线程持有的次数+获取次数acquires
int nextc = c + acquires;
if (nextc < 0) // overflow
//如果锁被线程连续持有次数,小于0,则超出,一个线程可以连续持有锁的最大次数
//抛出异常
throw new Error("Maximum lock count exceeded");
//否则,设置锁状态,返回true
setState(nextc);
return true;
}
//锁被持有,且持有者非当前线程,返回false,获取锁失败。
return false;
}
//尝试释放锁,releases释放次数
protected final boolean tryRelease(int releases) {
//获取释放releases次锁后的锁状态。
int c = getState() - releases;
//如果当前线程非锁持有者,抛出状态监控异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果锁持有者线程释放,releases次后,锁状态为打开
if (c == 0) {
//释放锁成功
free = true;
//设置锁持有者为NULL
setExclusiveOwnerThread(null);
}
//如果,释放releases次后,线程仍持有锁,设置锁状态,释放失败。
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();
}
// Methods relayed from outer class
//获取锁持有者线程,无持有者,则为null
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//获取线程连续持有锁的次数,如果是当前线程持有锁,则返回state,否则为0
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
//锁是否被持有
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes this lock instance from a stream.
* @param s the stream
*/
//反序列化锁,设置锁为打开状态
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
//创建可重入锁,默认为非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
//根据公平锁与非公平锁标志,创建相应的锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
static final class NonfairSync extends Sync {}
static final class FairSync extends Sync {}
}
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//先以CAS方式获取锁,如果获取成功,设置当前线程为锁,持有者
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//否则,这一步我们单看
acquire(1);
}
//尝试获取锁,acquires次,一般为1
protected final boolean tryAcquire(int acquires) {
//以非公平的方式获取锁
return nonfairTryAcquire(acquires);
}
}
/**
* 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}.
*尝试以独占模式,获取锁,忽略中断。至少尝试一次,获取锁,成功则返回,
获取失败,添加到同步等待队列,可能重复的blocking and unblocking,
尝试获取锁,直到成功,用于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();
}
//待父类扩展
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
addWaiter(Node.EXCLUSIVE)
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
addWaiter(Node.EXCLUSIVE)
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
创建独占或共享模式节点到同步等待队列中
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
selfInterrupt();
/**
* Convenience method to interrupt current thread.
*/
private static void selfInterrupt() {
Thread.currentThread().interrupt();
}
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//这个过程,前面说过//如果获取锁失败,则添加独占模式节点,
到队列中,自旋,队列头部节点尝试获取锁,
如果尝试获取失败,检查是否可以中断当前线程,如果可以,则中断当前线程。
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
这个函数与SYNC的nonfairTryAcquire方法基本相同
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//首先检查是否有前继节点,如果没有,则获取锁
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;
}
}
/**
* Performs non-fair tryLock. tryAcquire is
* implemented in subclasses, but both need nonfair
* try for trylock method.
*/
//在非公平锁的尝试获取锁方法中,会调用nonfairTryAcquire
//acquires为尝试获取次数,一般为1
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
int c = getState();//获取锁状态
if (c == 0) {//如果没有线程持有锁
if (compareAndSetState(0, acquires)) {
//尝试获取锁,如果获取成功,则设置锁的持有者,为当前线程,返回ture
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
//如果锁被线程持有,判断持有者是不是当前线程;
//如果当前线程是锁的持有者,则锁被当前线程持有的次数+获取次数acquires
int nextc = c + acquires;
if (nextc < 0) // overflow
//如果锁被线程连续持有次数,小于0,则超出,一个线程可以连续持有锁的最大次数
//抛出异常
throw new Error("Maximum lock count exceeded");
//否则,设置锁状态,返回true
setState(nextc);
return true;
}
//锁被持有,且持有者非当前线程,返回false,获取锁失败。
return false;
}
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//这个过程,前面说过
//如果获取锁失败,则添加独占模式节点,
到队列中,自旋,队列头部节点尝试获取锁,如果获取成功,设置当前节点为头节点;
如果尝试获取失败,检查是否可以中断当前线程,如果可以,则中断当前线程。
acquire(1);
}
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//先以CAS方式获取锁,如果获取成功,设置当前线程为锁,持有者
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
/**
* Acquires the lock.
*
* Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
* 如果锁没有被其他线程锁持有,则立即返回,锁持有锁为1
*
If the current thread already holds the lock then the hold
* count is incremented by one and the method returns immediately.
*如果当前线程已经持有锁,则所持有锁自增1
*
If the lock is held by another thread then the
* current thread becomes disabled for thread scheduling
* purposes and lies dormant until the lock has been acquired,
* at which time the lock hold count is set to one.
*/
如果锁被其他线程所持有,则当前线程自旋,知道获取锁
public void lock() {
//委托给同步器
sync.lock();
}
/**
* Acquires the lock only if it is not held by another thread at the time
* of invocation.
* 如果锁没有被其他线程所持有,则获取锁成功
*
Acquires the lock if it is not held by another thread and
* returns immediately with the value {@code true}, setting the
* lock hold count to one. Even when this lock has been set to use a
* fair ordering policy, a call to {@code tryLock()} [i]will[/i]
* immediately acquire the lock if it is available, whether or not
* other threads are currently waiting for the 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).
*如果锁没有被其他线程持有,则获取锁成功,立即返回true,锁持有数设为1.
当我们获取锁的策略为公平策略时,尝试获取锁时,如果锁可用,则获取成功,
无论其他线程和当前线程是否在等待锁。如果必须要保持公平可以用tryLock(long, TimeUnit)
方法。
*
If the current thread already holds this lock then the hold
* count is incremented by one and the method returns {@code true}.
*
*
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 lock was already held by the current
* thread; and {@code false} otherwise
*/
//以非公平方式尝试获取锁
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//以公平方式获取锁,其实公平方式,也不一定能保证绝对的公平,前面讲AQS说过
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
/**
* Acquires the lock unless the current thread is
* {@linkplain Thread#interrupt interrupted}.
*以可中断方式获取锁,当线程获取锁失败,则中断,当线程中断状态被消除时,
可以尝试获取锁。
*
Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
* 如果锁没有被其他线程锁持有,则立即返回,锁持有锁为1
*
If the current thread already holds this lock then the hold count
* is incremented by one and the method returns immediately.
*如果当前线程已经持有锁,则所持有锁自增1
*
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:
*
* [list]
当锁被其他线程持有,则自旋,尝试获取锁,直到以下两种条件发生
*
*
The lock is acquired by the current thread; or
* 锁被当前线程获取
* Some other thread {@linkplain Thread#interrupt interrupts} the
* current thread.
* 其他线程中断当前线程
* [/list]
*
* If the lock is acquired by the current thread then the lock hold
* count is set to one.
*
*
If the current thread:
*
* [list]
*
*
has its interrupted status set on entry to this method; or
*
* is {@linkplain Thread#interrupt interrupted} while acquiring
* the lock,
*
* [/list]
*
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* 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);
}
/**
* 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}.
*
以独占模式获取锁,如线程被中断,则aborting。
* @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);
}
/**
* Acquires in exclusive interruptible mode.
* @param arg the acquire argument
*/
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
//添加独占模式节点,到同步等待队列
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
//以自旋方式,这个过程与acquireQueued相似
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
//如果失败,移除取消等待的线程节点。
cancelAcquire(node);
}
}