在 JDK1.6 之前,JDK 中实现同步我们只能使用 synchronized 关键字,尽管从 JDK1.5 开始 Java 官方对重量级的 synchronized 进行了升级,引入了偏向锁及自旋锁,使得语言层面的锁更加的轻量和高效,但是始终不能满足用户对锁灵活控制的需求,所以才有了 ReentrantLock 的出现。
ReentrantLock.lock()
public void lock() {
sync.lock();
}
NonfairSync.lock()
final void lock() {
// 非公平锁,上来就去尝试获取锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 获取失败,进入自旋获取锁的逻辑
acquire(1);
}
AbstractQueuedSynchronizer.acquire()
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
// 再次非公平的尝试获取锁失败,将当前线程构建成 Node 节点,加入等待队列
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
NonfairSync.tryAcquire()
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
Sync.nonfairTryAcquire()
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();// state 表示锁被获取的次数
// 这里再次体现了锁的非公平性,此时不是去乖乖的排队等待,而是再次尝试获取锁
if (c == 0) {// 说明此时锁被释放
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");
// 因为当前线程即为锁的 owner,因此这里不需要 CAS,而是直接更新
setState(nextc);
return true;
}
return false;
}
AbstractQueuedSynchronizer.addWaiter()
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;
// 等待队列是一个双向链表
// 如果尾节点不为空,将当前线程构建成的节点通过 CAS 添加到队列的尾部
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 如果队列为空,或者加入队列的时候失败,进入这里的逻辑
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 尾节点为空,说明此时是空队列,构造一个空节点作为头节点
// 头节点代表当前获得锁的线程对应的节点
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {// 如果尾不为空,当前线程会自旋入队
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
AbstractQueuedSynchronizer.acquireQueued()
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;
}
// 如果前驱节点不是头节点,或者上面的获取锁操作失败,会走到这里
// 通过 shouldParkAfterFailedAcquire 方法我们可以知道:
// 当前线程会把前驱线程的 waitStatus 值设置为 -1,代表唤醒
// 当前驱节点获得锁并释放后,会根据这个 waitStatus 去判断是否需要唤醒后继线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 如果线程被挂起的过程中被中断过,会返回 true
// 最终会在 acquire() 方法中调用 selfInterrupt() 方法,
// 执行线程的 interrupt() 方法进行中断
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
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.
*/
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.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
// 将当前线程挂起
LockSupport.park(this);
// 返回当前线程的中断标识
return Thread.interrupted();
}
当我们使用 ReentrantLock 的时候,如果不指定,默认会使用非公平锁。并发大师 Doug Lea 将非公平锁作为默认的可重入锁的实现,这里比较侧重的是锁的获取效率,我们在实际使用时可以根据具体需求进行选择。
public ReentrantLock() {
sync = new NonfairSync();
}
下面我们通过与公平锁的对比,来分析一下非公平锁的非公平性到底体现在哪里。
final void lock() {
acquire(1);
}
final void lock() {
// 上来先去试着获取锁,不管锁是否被获取过
// 不成功再说,万一成功了呢
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
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;
}
}
// 如果锁没有被释放,判断当前线程是否是锁的 owner,如果是,直接重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {// 锁被释放
// 不管是否有前驱线程,直接去获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果锁没有被释放,判断当前线程是否是锁的 owner,如果是,直接重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
public void unlock() {
sync.release(1);
}
AbstractQueuedSynchronizer.release()
public final boolean release(int arg) {
if (tryRelease(arg)) {// 先去释放锁
Node h = head;
if (h != null && h.waitStatus != 0)
// 如果代表锁 owner 的头节点存在,并且 waitStatus 不为初始值,
// 唤醒后继节点
unparkSuccessor(h);
return true;
}
return false;
}
Sync.tryRelease()
protected final boolean tryRelease(int releases) {
// 重入的情况,进来几次就要退出几次,锁才能最终被释放
int c = getState() - releases;
// 当前线程不是锁的 owner,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
AbstractQueuedSynchronizer.unparkSuccessor()
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;
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;
// 如果头节点的后继节点为空或者已经被取消
if (s == null || s.waitStatus > 0) {
// 从尾节点向前遍历,找到最后一个需要被唤醒的节点,
// 即队列中的第一个没有被取消的节点,然后将其唤醒
// 这里有一个疑问,为什么要从前往后进行遍历?
// addWaiter() 和 enq() 两个方法中,节点入队的时候,
// 先执行的都是 node.prev = pred; 然后再通过 CAS 将当前节点设置成尾节点,
// 设置成功后再将前驱节点的后继节点设置为当前节点。
// 因此如果从前往后遍历,可能会出现断链的情况
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
关于锁的释放,逻辑比较简单,没什么可说。这里说一下 AQS 中的 waitStatus 的含义,理解其含义对 AQS 体系的源码解读会有所帮助。
源码中对 waitStatus 的解释如下:
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;// 被取消的线程的状态
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;// 标识后继线程需要被唤醒
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;// 标识当前线程等待在 condition 条件上
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;// 用于共享锁,标识传播共享标识
/**
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;