ReentrantLock 可重入锁,实现Lock 接口。使用同步器实现加锁解锁,锁等待。
- 加锁过程如下:
- 代码解析:
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
//同步器,继承AQS
private final Sync sync;
/**
* AQS 加锁
* 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.
*/
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.
*/
// 非公平模式尝试获取锁
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//如果没有线程持有锁,即同步器的同步状态state等于0
if (c == 0) {
//CAS 设置同步器状态,成功则获取锁
if (compareAndSetState(0, acquires)) {
//同步器持有的排他线程设置为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//同步器持有的排他线程等于当前线程,则锁重入。
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
//可重入次数不能大于int的最大值,否则则会异常。
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//获取锁成功,设置同步器 的同步状态
setState(nextc);
return true;
}
//CAS设置失败,获取锁失败
return false;
}
//尝试释放锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果当前线程不等于同步器持有 的排他线程,则抛出异常。
//这相当于当前线程试图释放不属于自己锁的线程,自然不能释放
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//同步器状态减去传来的参数为0,则说明当前线程只获得一次锁。
if (c == 0) {
// 返回true即释放锁成功
free = true;
setExclusiveOwnerThread(null);
}
//如果c不等于0,则说明当前线程多次获得锁,即可重入获取。返回false
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
// 获取持有锁的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//获取当前线程持有锁的计数
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
//从流中重构实例
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
// 通过维护一个先进先出的队列,实现锁或同步器。
* <pre>
* +------+ prev +-----+ +-----+
* head | | <---- | | <---- | | tail
* +------+ +-----+ +-----+
* </pre>
加锁过程:
// 1.调用内部类NonfairSync的lock方法 默认非公平锁的模式
public void lock() {
sync.lock();
}
// 继承sync,实现非公平锁。实现方式为来一个线程调用lock方法时,
// 就让这个线程先尝试去获取锁,获取不到则加入等待队列。
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() {
// 2 尝试设置同步状态值为1,0代表state的状态没有线程持有锁,也没有等待获取锁的队列
// 即当前线程是第一个尝试获取锁的。设置成功则将当前线程持有该排它锁。
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//3.否则尝试获取锁,失败则加入等待队列以独占模式
acquire(1);
}
// 尝试获取锁 ,失败则尝试加入等待队列
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//获取锁失败且线程被中断则调用线程中断方法
selfInterrupt();
}
// 4 非公平方式尝试获取锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//5 以非公平的模式尝试获取锁 此方法来自sync
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取当前锁对象状态
int c = getState();
// 0 代表没有任何线程获得锁
if (c == 0) {
// CAS尝试设置state的值,成功则将当前线程设置为独占模式下持有
// 的线程。
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//判断当前线程是否是持有独占锁的线程,是则设置同步状态。
// 这里就实现了锁的可重入特性。同一个线程获得锁后再次获取锁。
// 会将同步状态加1
else if (current == getExclusiveOwnerThread()) {
// 设置下一个同步状态值
int nextc = c + acquires;
//小于0 则栈溢出 ,如果一个线程很多次获取同一个锁,超过了锁的最大数,则抛出异常
//超过最大锁数 ,int类型的最大值加1后等于int类型的最小值,负数。
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 设置同步状态值
setState(nextc);
return true;
}
return false;
}
// // 6 上一步获取失败后,当前线程加入等待队列
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;
// CAS 尝试 将新节点设置为尾结点
if (compareAndSetTail(pred, node)) {
// 成功将当前尾结点的next指针指向新节点。返回新节点
pred.next = node;
return node;
}
}
//对列尾结点为空,使用enq尝试加入节点
enq(node);
return node;
}
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
// 初始化对列,并把node加入队列尾部
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//尾结点为空,说明对列为空,则初始化当前对列
if (t == null) { // Must initialize
// cas 尝试设置一个头结点,成功则将头结点赋值给尾结点。继续循环
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 当前节点的上一个节点设置为当前的尾结点
node.prev = t;
// CAS尝试将node 设置为尾结点。成功
// 则将之前尾结点的下一个节点设置为node
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
/**
* Acquires in exclusive uninterruptible mode for thread already in
* queue. Used by condition wait methods as well as acquire.
*
* @param node the node
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
//加入等待队列后,再次尝试获取锁
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 循环获取知道当前node节点的上一个节点为头结点。
// 然后尝试获取锁。这就相当于是自旋方式获取锁
for (;;) {
//获取node 节点的上一个节点,为空抛出异常
final Node p = node.predecessor();
//上一个节点为头结点则尝试获取锁
if (p == head && tryAcquire(arg)) {
//获取成功则将node 节点设置为头结点,头结点
//头结点的thread为null,prev也是null。
// 所以node没有指向p的引用了。
setHead(node);
// 之前头结点即p的下一个节点指向null,那么p没有指向node的引用了,
// 那么此时p节点的状态就是一个独立的对象,没有任何对象有引用指向p,
//垃圾回收的可达性算法说明该节点可以回收了。
//
p.next = null; // help GC
failed = false;
return interrupted;
}
// 尝试设置等待状态为-1,为-1则检查当前线程是否中断。
// 中断成功设置中断标识为true. 线程阻塞成功且已经中断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
// 获取失败后当前线程是否应该阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// -1 代表可以阻塞该线程。
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
//循环对列获取一个不大于0 的节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
// 等于0 或小于0 不等于-1 则CAS尝试将节点等待状态改为-1
// 这样下次可以阻塞该线程
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
// 调用park 阻塞当前线程
private final boolean parkAndCheckInterrupt() {
//阻塞当前线程
LockSupport.park(this);
//当前线程已经被中断返回true. 未中断 false
return Thread.interrupted();
}
// 解锁过程
// 1 。调用unlock
public void unlock() {
sync.release(1);
}
// 解锁
public final boolean release(int arg) {
// 尝试解锁,如果当前线程不是持有锁的线程抛出异常
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
//
}
// 尝试解锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果当前线程不是持有锁的线程抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 状态为0,说明该线程不再持有锁
// 不等于0,则说明该线程重复持有锁,需要多次解锁直到状态为0则不再持有锁。
// 如果一个线程多次持有一个锁,会造成阻塞。
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//设置状态
setState(c);
return free;
}
// 尝试唤醒下一个等待锁的线程
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
// 当前节点等待状态小于0,则设置等待状态为0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
// 下一个节点为空,等待状态大于0
if (s == null || s.waitStatus > 0) {
s = null;
// 循环找到一个状态小于等于0的最靠近头结点的节点
// 且该节点不等于当前节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 下一个节点不为空,则唤醒阻塞的节点
if (s != null)
LockSupport.unpark(s.thread);
}
// 锁释放过程: 尝试释放线程持有的锁,状态减一,减一后状态为0 ,则设置当前锁对象持有的线程为空。
// 锁对象状态变为0,返回true。
// 锁释放成功后,检查头结点不为空且等待状态不等于0,
// 则 尝试唤醒下一个节点,则下一个节点可以尝试获取锁。
//
}
-
这样一个完整的加锁过程。总结来看,
-
如果获取锁的时候,state 状态为0 , CAS 设置state状态变为1 成功,则当前线程获得锁。cas设置失败,则再次尝试获取锁。
-
获取当前 state状态为0,则再次尝试CAS设置状态,成功则将当前线程设置为独占线程,获得锁。
-
不等于0 则判断当前线程是否为持有锁的线程,是,则可以再次获得锁。将state状态加1. 加1后小于0,则抛出超出锁数量超出异常。设置状态后返回true,则当前线程获得锁继续执行。
-
获取锁失败,则以独占模式加入等待队列。如果尾结点存在则当前节点插入作为尾结点。 不存在则使用enq初始化队列,new一个空节点作为头结点也是尾结点。 循环,再次将当前节点设为尾结点,返回成功。
-
然后如果当前节点的上一个节点时头结点,则尝试获取锁。
-
检查线程状态,查看线程是否应该被阻塞且中断标志是否为true。 如果满足两个条件,则获取锁成功后会调用线程的中断方法中断当前线程
-
即线程在循环等待获取锁的过程中可能被中断了,中断状态为true,此时线程没有真正中断。在获取锁成功后调用线程的中断方法。
-
如果没有中断则获取锁成功,继续执行。
-
特点:
-
通过 state 同步状态控制锁的获取,通过双向队列进行线程的排队获取锁。
-
通过 持有线程的引用实现锁的可重入特性及锁的准确释放及谁获取的锁谁释放。
-
通过CAS 来设置同步状态,头节点,尾节点等。实现了一个轻量级的可重入锁。
-
通过尝试加锁的时候是否判断有线程等待的比当前获取锁的线程时间长来实现公平性。
-
加入等待队列的尾插法也体现了公平性。因为获取锁总是从头节点开始。