Java源码总结锁部分解读
Posted 低调的洋仔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java源码总结锁部分解读相关的知识,希望对你有一定的参考价值。
LockSupport
内部采用了Unsafe中的方法来实现的,使用其实现阻塞的相关操作,调用其park方法来实现线程的阻塞。Unsafe主要提供了硬件级别原子操作。
1.Java线程一般与操作系统进程是一对一的关系,比如在linux平台,对应的是linux的轻量级进程(也就是linux的线程)。
2.linux线程的调度和具体调度器有关,比如CFS调度器下,所有待调度的线程按照nice值排列在一棵红黑树中;系统级的休眠与唤醒依赖系统信号,如果一个线程休眠,会被从红黑树移动到一个等待队列中,被唤醒后再移动回来,大体的过程是这样的。
ReentrantLock
abstract static class Sync extends AbstractQueuedSynchronizer
里面有个Sync的实例。
static final class NonfairSync extends Sync
非公平的锁实现继承了Sync。
非公平锁的实现,
一开始尝试将自己设置上获得锁的状态。
然后如果尝试失败,就调用acquire方法来请求独占锁。
tryAcquire中设置成为了可重入式的,针对当前线程而言。
final void lock()
// 直接CAS操作尝试获取锁
// 相当于一个插队的操作(可能会出现AQS中有线程在等待,但是当前的线程竞争成功)
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);// 如果失败了就调用acquire(1),独占请求方法,
// 这里实质都是独占锁,但是在当前的syn中实现的tryAcquire中设置为当前线程可重入了。
/**
* public final void acquire(int arg)
* 这里调用的方法即下面的tryAcquire方法,该方法又调用了nonfairTryAcquire
* 如果没有获取锁的就自己尝试获取锁,否则如果当前的锁已经被持有了的话就判断是不是当前线程,如果是当前线程累计重入次数,
* 否则已经持有了锁但是不是自己的话直接返回false
*
*
* if (!tryAcquire(arg) &&
* acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
* selfInterrupt();
*
*/
protected final boolean tryAcquire(int acquires)
return nonfairTryAcquire(acquires);
在acquire这个方法里面又再次的调用了tryAcquire这个方法,在非公平锁实现里面是调用了父类Sync中的nonfairTryAcquire这个方法,这个方法主要是进行了这样几个操作:
1. 如果没有人拿到锁的话,我就来进行尝试拿到锁,使用Cas进行竞争。
2.如果有人拿到锁的话,我判断是不是我当前的线程拿到的,如果是的话,我就继续拿到这个锁,继续执行,当然状态会进行累加,也就是累计了我当前线程拿到锁的次数了。
final boolean nonfairTryAcquire(int acquires)
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0)
// 当前没有任何的线程获取锁(锁可用)就尝试设置锁
if (compareAndSetState(0, acquires))
// 将当前线程放到AQS中去
setExclusiveOwnerThread(current);
return true;
//如果锁已经被持有,那么判断一下持有锁的线程是否为当前线程。
else if (current == getExclusiveOwnerThread())
// 如果是当前线程累计重入次数
int nextc = c + acquires;
if (nextc < 0) // overflow 溢出了
throw new Error("Maximum lock count exceeded");
setState(nextc); // 设置下State
return true;
// 如果已经持有了但是不是当前的线程持有的返回false
return false;
如果调用tryAcquire这个方法后还是没有拿到锁,那么会继续调用if条件语句中的下一个方式来获取锁。
public final void acquire(int arg)
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
这个地方&&后面的这个表达式表示了嵌套的两个方法。
先看下addWaiter方法:
这个方法主要用来将枷锁的请求入队,首先会尝试快速的入队,通过Cas来快速的将自己设置到链表的尾部。如果设置成功那么直接返回该节点就好了,但是如果不成功,那么需要调用enq()方法。
private Node addWaiter(Node mode)
//根据当前线程和模式创建一个Node。
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;
//尝试将当前Node设置(原子操作)为同步等待队列的尾节点。
if (compareAndSetTail(pred, node))
//如果设置成功,完成链接(pred的next指向当前节点)。
// 所以这个地方next=null并不一定就是尾节点。可能还没设置为node节点
pred.next = node;
//返回当前节点。
return node;
//如果同步等待队列尾节点为null,或者快速入队过程中设置尾节点失败,
//进行正常的入队过程,调用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
*
* 入队
*/
private Node enq(final Node node)
for (;;)
Node t = tail;
/*
* 如果同步等待队列尾节点为null,说明还没有任何线程进入同步等待队列,
* 这时要初始化同步等待队列:创建一个(dummy)节点,然后尝试将这个
* 节点设置(CAS)为头节点,如果设置成功,将尾节点指向头节点
* 也就是说,第一次有线程进入同步等待队列时,要进行初始化,初始化
* 的结果就是头尾节点都指向一个哑(dummy)节点。
*/
if (t == null) // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
else
//将当前(线程)节点的前驱节点指向同步等待队列的尾节点。
node.prev = t;
//注意节点拼接到同步等待队列总是分为3个步骤:1.将其prev引用指向尾节点 2.尝试将其设置为尾节点 3.将其prev节点(第2步之前的尾节点)的next指向其本身。
//所以一个节点为尾节点,可以保证prev一定不为null,但无法保证其prev的next不为null。所以后续的一些方法内会看到很多对同步等待队列的反向遍历。
//尝试将当前节点设置为同步等待队列的尾节点。
if (compareAndSetTail(t, node))
//如果成功,将之前尾节点的后继节点指向当前节点(现在的尾节点),完成节点拼接。
t.next = node;
//返回之前的尾节点。
return t;
节点入队成功后,不断的循环,并且一旦自己是头节点的时候,就尝试获取锁。最后几行中会判断自己是不是应该阻塞住,如果符合条件的话就将自己park住,然后自旋的过程结束阻塞直到被激活,一般一个头结点在获取到锁要释放的时候会激活这个头结点的线程继续去获取锁。
/**
* 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;
for (;;)
// 当前节点的前驱节点
final Node p = node.predecessor();
/*
* 检测p是否为头节点,如果是,再次调用tryAcquire方法
* (这里可以体现出acquire方法执行过程中tryAcquire方法
* 至少被调用一次)。
*/
if (p == head && tryAcquire(arg))
//如果p节点是头节点且tryAcquire方法返回true。那么将
//当前节点设置为头节点。
//从这里可以看出,请求成功且已经存在队列中的节点会被设置成头节点。
setHead(node);
//将p的next引用置空,帮助GC,现在p已经不再是头节点了。
p.next = null; // help GC
//设置请求标记为成功
failed = false;
//传递中断状态,并返回。
return interrupted;
//如果p节点不是头节点,或者tryAcquire返回false,说明请求失败。
//那么首先需要判断请求失败后node节点是否应该被阻塞,如果应该
//被阻塞,那么阻塞node节点,并检测中断状态。
// 前面先判断是不是应该阻塞,如果返回false当前if运行结束,否则,继续执行parkAndCheckInterrupt方法,该方法可以进行阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
//如果有中断,设置中断状态。
interrupted = true;
finally
//最后检测一下如果请求失败(异常退出),取消请求。
if (failed)
cancelAcquire(node);
当锁在release的时候会调用unparkSuccessor这个方法
public final boolean release(int arg)
if (tryRelease(arg))
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
return false;
内部会调用unpark方法激活等待的线程。
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.
*/
/*
* 如果node的等待状态为负数(比如:可能需要一个信号),尝试去清空
* "等待唤醒"的状态(将状态置为0),即使设置失败,或者该状态已经
* 被正在等待的线程修改,也没有任何影响。
*/
// 获取节点的等待状态:condition、cancelled
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的后继节点,一般来说就是node的next引用指向的节点。
* 但如果next指向的节点被取消或者为null,那么就同步等待队列的队尾反向查找离
* 当前节点最近的且状态不是"取消"的节点。
*/
Node s = node.next;
if (s == null || s.waitStatus > 0)
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);
公平锁
final void lock()
acquire(1);
lock同样调用了acquire方法。
* 独占模式下进行请求,忽略中断。方法实现中至少会调用一次tryAcquire方法,
* 请求成功后方法返回。否则当前线程会排队,可能会重复的阻塞和解除阻塞,
* 执行tryAcquire方法,直到成功。这个方法可以用来实现Lock的lock方法。
*
* @param arg the acquire argument. 这个值被传递给tryAcquire方法,值在
* 这里并没有实际意义,如果基于AQS实现自己的同步机制(可能要实现
* tryAcquire方法),可以灵活利用这个值。
*/
public final void acquire(int arg)
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
然后tryLock调用自身的方法。
这个地方只要自己当前存在锁了,就将自己去入队处理了,这里保证次序了,只要队列不是空的你就不能获取锁,所以是公平的,但是非公平的它是一旦调用枷锁的操作那我马上就去尝试获取锁,不管你到底是不是有请求在获取锁。
/**
* Fair version of 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();
// 当前重入数量为0
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;
ReentrantReadWriteLock
实现了ReadWriteLock,Serializable接口。
abstract static class Sync extends AbstractQueuedSynchronizer
AQS的实现类Sync类。
public static class ReadLock implements Lock, java.io.Serializable
public static class WriteLock implements Lock, java.io.Serializable
读锁和写锁。
读锁 ReadLock
锁定是lock方法,调用了AQS的acquireShared()的方法。
public void lock()
sync.acquireShared(1);
调用了AQS中的acquireShared方法
* acquireShared方法中首先调用tryAcquireShared方法,
* 如果tryAcquireShared返回值大于等于0,说明请求成功,直接返回;
* 否则,继续调用doAcquireShared方法。先看一下tryAcquireShared方法,
* 该方法在AQS中并没有具体实现,同样开放出来,交由子类去实现。
*/
public final void acquireShared(int arg)
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
调用实现子类中的tryAcquireShared()方法。
基本思路就是先看是不是存在独占锁,存的话就直接返回-1,认为获取失败。
如果不存在独占锁,就看当前存在不存在共享锁的,已经存在了的话就直接进行累加,如果不存在自己是第一个,那么就累计共享锁的状态,并设置自己当前的线程为firstReader。
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.
*/
/*
* 共享模式的请求流程:
* 1. 如果有其他线程持有写锁,方法失败。
* 2. 如果重入次数超过最大值,抛错。
* 3. 否则,判断当前队列策略是否需要阻塞读请求,
* 如果不需要,尝试CAS修改状态,修改成功的话,
* 更新重入次数,请求成功;
* 4. 如果第3步失败,调用fullTryAcquireShared方法。
*/
Thread current = Thread.currentThread();
int c = getState();
// 存在写锁的时候失败
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT))
// 共享锁为0个
if (r == 0)
firstReader = current;
firstReaderHoldCount = 1;
//
else if (firstReader == current)
firstReaderHoldCount++;
// 缓存holdcounter
else
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
return 1;
// 如果上面的全都失败了就执行这个语句
return fullTryAcquireShared(current);
AQS中的锁的表示,高16位表示的是共享锁, 低16位表示的是独占锁。
// 在AQS章节中介绍到AQS中有一个state字段(int类型,32位)
// 用来描述有多少线程获持有锁。在独占锁的时代这个值通常是0或者1(如果是重入的就是重入的次数),
//在共享锁的时代就是持有锁的数量。在上一节中谈到,ReadWriteLock的读、写锁是相关但是又不一致的,
//所以需要两个数来描述读锁(共享锁)和写锁(独占锁)的数量。显然现在一个state就不够用了。
//于是在ReentrantReadWrilteLock里面将这个字段一分为二,高位16位表示共享锁的数量,
//低位16位表示独占锁的数量(或者重入数量)。2^16-1=65536,这就是上节中提到的为什么共享锁
//和独占锁的数量最大只能是65535的原因了。
上面的方式全部都没有成功的话就调用下面的方法进行获取。
说白了就是尝试获取共享锁,一旦有读锁即独占的话,就直接退出,获取共享锁失败,否则的话就去尝试获取读锁,但是如果这个还是没成功的话就进入自旋操作,不断的来循环执行,直到获取到共享锁或者因为存在了独占锁而直接退出认为自己获取共享锁失败。
/**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*/
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();
// 依旧是检查排它锁是不是0个
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 != current.getId())
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
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))
// 共享的数量=0
if (sharedCount(c) == 0)
firstReader = current;
firstReaderHoldCount = 1;
else if (firstReader == current)
firstReaderHoldCount++;
//
else
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
// 计数重入个数
rh.count++;
cachedHoldCounter = rh; // cache for release
return 1;
再次回到原来调用的地方
* acquireShared方法中首先调用tryAcquireShared方法,
* 如果tryAcquireShared返回值大于等于0,说明请求成功,直接返回;
* 否则,继续调用doAcquireShared方法。先看一下tryAcquireShared方法,
* 该方法在AQS中并没有具体实现,同样开放出来,交由子类去实现。
*/
public final void acquireShared(int arg)
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
如果获取失败的话还会调用一个方法。
进入自旋,直到获取到共享锁为止,尝试几次之后应该会挂起自己,等待其他的线程一般是头结点的线程释放锁的时候会唤醒后面的线程来取得锁。
/**
* Acquires in shared uninterruptible mode.
* @param arg the acquire argument
*
*/
private void doAcquireShared(int arg)
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try
boolean interrupted = false;
for (;;)
final Node p = node.predecessor();
if (p == head)
int r = tryAcquireShared(arg);
if (r >= 0)
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
finally
if (failed)
cancelAcquire(node);
写锁 writeLock
public void lock()
sync.acquire(1);
请求AQS中acquire方法执行获取锁的动作。
* 独占模式下进行请求,忽略中断。方法实现中至少会调用一次tryAcquire方法,
* 请求成功后方法返回。否则当前线程会排队,可能会重复的阻塞和解除阻塞,
* 执行tryAcquire方法,直到成功。这个方法可以用来实现Lock的lock方法。
*
* @param arg the acquire argument. 这个值被传递给tryAcquire方法,值在
* 这里并没有实际意义,如果基于AQS实现自己的同步机制(可能要实现
* tryAcquire方法),可以灵活利用这个值。
*/
public final void acquire(int arg)
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
这个地方就和ReentrantLock中的方法一样了,过程基本上是重复了ReentrantLock中的方法。
看下自己实现的tryLock方法。
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);
// c不为0且w=0 说明读锁持有次数不为0
if (c != 0)
// (Note: if c != 0 and w == 0 then shared count != 0)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
setState(c + acquires);
return true;
// 如果c是0的话就是说无读锁
// 判断是否应该阻塞写锁,如果需要阻塞的话失败,或者CAS设置state无法设置其值的话失败
// 成功则设置当前线程获得写锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
AQS
* 说明:节点类Node内部定义了一些常量,如节点模式、等待状态;Node内部有指向其前驱和后继节点的引用(类似双向链表);
* Node内部有保存当前线程的引用;Node内部的nextWaiter域在共享模式下指向一个常量SHARED,
* 在独占模式下为null或者是一个普通的等待条件队列(只有独占模式下才存在等待条件)。
*/
static final class Node
/** Marker to indicate a node is waiting in shared mode
* 节点共享模式下的常量
* */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode
* 节点独占模式下的常量
* */
static final Node EXCLUSIVE = null;
/** 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;
/**
* 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).
*
* 等待状态域, 取以下值:
* SIGNAL: 当前节点的后继节点已经(或即将)被阻塞,所以如果当前节点释放(控制权)
* 或者被取消时,必须唤醒其后继节点。为了避免竞争,请求方法必须首先
* 声明它们需要一个信号,然后(原子的)调用请求方法,如果失败,当前线程
* 进入阻塞状态。
* CANCELLED: 表示当前节点已经被取消(由于超时或中断),节点一旦进入被取消状态,就
* 不会再变成其他状态了。具体来说,一个被取消节点的线程永远不会再次被
* 阻塞
* CONDITION: 表示当前节点正处在一个条件队列中。当前节点直到转移时才会被作为一个
* 同步队列的节点使用。转移时状态域会被设置为0。(使用0值和其他定义值
* 并没有关系,只是为了简化操作)
* PROPAGATE: 表示一个共享的释放操作(releaseShared)应该被传递到其他节点。该状态
* 值在doReleaseShared过程中进行设置(仅在头节点),从而保证持续传递,
* 即使其他操作已经开始。
* 0: None of the above
*
* 这些状态值之所以用数值来表示,目的是为了方便使用,非负的值意味着节点不需要信号(被唤醒)。
* 所以,一些代码中不需要针对特殊值去做检测,只需要检查符号(正负)即可。
*
* 针对普通的同步节点,这个域被初始化为0;针对条件(condition)节点,初始化为CONDITION(-2)
* 需要通过CAS操作来修改这个域(如果可能的话,可以使用volatile写操作)。
*/
volatile int waitStatus;
/**
* Link to predecessor node that current node/thread relies on
* for checking waitStatus. Assigned during enqueing, and nulled
* out (for sake of GC) only upon dequeuing. Also, upon
* cancellation of a predecessor, we short-circuit while
* finding a non-cancelled one, which will always exist
* because the head node is never cancelled: A node becomes
* head only as a result of successful acquire. A
* cancelled thread never succeeds in acquiring, and a thread only
* cancels itself, not any other node.
*
* 指向当前节点的前驱节点,用于检测等待状态。这个域在入队时赋值,出队时置空。
* 而且,在取消前驱节点的过程中,可以缩短寻找非取消状态节点的过程。由于头节点
* 永远不会取消(一个节点只有请求成功才会变成头节点,一个被取消的节点永远不可
* 能请求成功,而且一个线程只能取消自己所在的节点),所以总是存在一个非取消状态节点。
*
*/
volatile Node prev;
/**
* Link to the successor node that the current node/thread
* unparks upon release. Assigned during enqueuing, adjusted
* when bypassing cancelled predecessors, and nulled out (for
* sake of GC) when dequeued. The enq operation does not
* assign next field of a predecessor until after attachment,
* so seeing a null next field does not necessarily mean that
* node is at end of queue. However, if a next field appears
* to be null, we can scan prev's from the tail to
* double-check. The next field of cancelled nodes is set to
* point to the node itself instead of null, to make life
* easier for isOnSyncQueue.
*
* 指向当前节点的后继节点,释放(控制权)时会唤醒该节点。这个域在入队时赋值,在跳过
* 取消状态节点时进行调整,在出队时置空。入队操作在完成之前并不会对一个前驱节点的
* next域赋值,所以一个节点的next域为null并不能说明这个节点在队列尾部。然而,如果
* next域为null,我们可以从尾节点通过前驱节点往前扫描来做双重检测。取消状态节点的
* next域指向自身,这样可以简化isOnSyncQueue的实现。
*/
volatile Node next;
/**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
* 使当前节点入队的线程。在构造构造的时候初始化,使用后置为null。
*/
volatile Thread thread;
/**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
* linked queue to hold nodes while they are waiting on
* conditions. They are then transferred to the queue to
* re-acquire. And because conditions can only be exclusive,
* we save a field by using special value to indicate shared
* mode.
*
* 指向下一个条件等待状态节点或者为特殊值(SHARED)。由于条件队列只有在独占模式下才
* 能访问,所以我们只需要一个普通的链表队列来保存处于等待状态的节点。它们在重新请
* 求的时候会转移到同步队列。由于条件只存在于独占模式下,所以如果是共享模式,就将
* 这域保存为一个特殊值(SHARED)。
*/
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode
*/
final boolean isShared()
return nextWaiter == SHARED;
/**
* Returns previous node, or throws NullPointerException if null.
* Use when predecessor cannot be null. The null check could
* be elided, but is present to help the VM.
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
Node() // Used to establish initial head or SHARED marker
Node(Thread thread, Node mode) // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
Node(Thread thread, int waitStatus) // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
Condition
说明下,其实Condition内部维护了等待队列的头结点和尾节点,该队列的作用是存放等待signal信号的线程,该线程被封装为Node节点后存放于此。
关键的就在于此,我们知道AQS自己维护的队列是当前等待资源的队列,AQS会在资源被释放后,依次唤醒队列中从前到后的所有节点,使他们对应的线程恢复执行。直到队列为空。
而Condition自己也维护了一个队列,该队列的作用是维护一个等待signal信号的队列,两个队列的作用是不同,事实上,每个线程也仅仅会同时存在以上两个队列中的一个,流程是这样的:
1. 线程1调用reentrantLock.lock时,线程被加入到AQS的等待队列中。
3. 线程1调用await方法被调用时,该线程从AQS中移除,对应操作是锁的释放。
2. 接着马上被加入到Condition的等待队列中,以为着该线程需要signal信号。先加入condition的队列然后才释放锁
4. 线程2,因为线程1释放锁的关系,被唤醒,并判断可以获取锁,于是线程2获取锁,并被加入到AQS的等待队列中。
5. 线程2调用signal方法,这个时候Condition的等待队列中只有线程1一个节点,于是它被取出来,并被加入到AQS的等待队列中。 注意,这个时候,线程1 并没有被唤醒。
6. signal方法执行完毕,线程2调用reentrantLock.unLock()方法,释放锁。这个时候因为AQS中只有线程1,于是,AQS释放锁后按从头到尾的顺序唤醒线程时,线程1被唤醒,于是线程1回复执行。
7. 直到释放所整个过程执行完毕。
可以看到,整个协作过程是靠结点在AQS的等待队列和Condition的等待队列中来回移动实现的,Condition作为一个条件类,很好的自己维护了一个等待信号的队列,并在适时的时候将结点加入到AQS的等待队列中来实现的唤醒操作。
看到这里,signal方法的代码应该不难理解了。
public final void signal()
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
public final void await() throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();// 将当前线程封装放入Condition自己维护的链表中去
int savedState = fullyRelease(node);// 释放当前线程占用的锁调用await前,当前线程是占有锁的
int interruptMode = 0;
//释放完毕后,遍历AQS的队列,看当前节点是否在队列不在 说明它还没有竞争锁的资格,所以继续将自己沉睡。直到它被加入到队列中,聪明的你可能猜到了,没有错,在singal的时候加入不就可以了?
while (!isOnSyncQueue(node))
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
//被唤醒后,重新开始正式竞争锁,同样,如果竞争不到还是会将自己沉睡,等待唤醒重新开始竞争。
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
doSignal将节点放入AQS中去。
private void doSignal(Node first)
do
//修改头结点,完成旧头结点的移出工作
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
while (!transferForSignal(first) &&//将老的头结点,加入到AQS的等待队列中
(first = firstWaiter) != null);
CountDownLatch
基于共享模式,AQS的共享锁。
基本的思路就是有一个count,然后实际上会创建一个Sync的内部类的实例,这个Sync实现了AQS。
public CountDownLatch(int count)
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
countDown
public void countDown()
sync.releaseShared(1);
共享锁减去1,tryReleaseShares是自己实现的、
public final boolean releaseShared(int arg)
if (tryReleaseShared(arg))
doReleaseShared();
return true;
return false;
protected boolean tryReleaseShared(int releases)
// Decrement count; signal when transition to zero
for (;;)
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
更新一下锁的状态,就是共享锁的数量了。
然后调用的doReleaseShared()方法,减少到了0的时候,就会唤醒正在阻塞的线程。
* 共享模式下的释放(控制权)动作 -- 唤醒后继节点并保证传递。
* 注:在独占模式下,释放仅仅意味着如果有必要,唤醒头节点的
* 后继节点。
*
*/
private void doReleaseShared()
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
/*
* 保证释放动作(向同步等待队列尾部)传递,即使没有其他正在进行的
* 请求或释放动作。如果头节点的后继节点需要唤醒,那么执行唤
* 动作;如果不需要,将头结点的等待状态设置为PROPAGATE保证
* 唤醒传递。另外,为了防止过程中有新节点进入(队列),这里必
* 需做循环,所以,和其他unparkSuccessor方法使用方式不一样
* 的是,如果(头结点)等待状态设置失败,重新检测。
*/
for (;;)
Node h = head;
//判断同步等待队列是否为空
if (h != null && h != tail)
//如果不为空,获取头节点的等待状态。
int ws = h.waitStatus;
//如果等待状态是SIGNAL,说明其后继节点需要唤醒
//尝试修改等待状态
if (ws == Node.SIGNAL)
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; //如果修改失败,重新循环检测。 // loop to recheck cases
unparkSuccessor(h); //如果修改成功,唤醒头节点的后继节点。
else if (ws == 0 &&//如果等待状态是0,尝试将其(头节点)设置为PROPAGATE
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // 如果设置失败,继续循环检测。 // loop on failed CAS
if (h == head) // 如果过程中头节点没有发生变化,循环退出;否则需要继续检测。 // loop if head changed
break;
await方法
public void await() throws InterruptedException
sync.acquireSharedInterruptibly(1);
调用了父类中的acquireSharedInterruptibly()方法、
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
调用tryAcquireShared()方法。如果当前的计数值是0的话就返回1否则的话就返回-1。
protected int tryAcquireShared(int acquires)
return (getState() == 0) ? 1 : -1;
如果不是0那么调用AQS中的方法。然后尝试几次获取共享锁后将自己挂起。
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try
for (;;)
final Node p = node.predecessor();
if (p == head)
int r = tryAcquireShared(arg);
if (r >= 0)
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
finally
if (failed)
cancelAcquire(node);
Semphore
public class Semaphore implements java.io.Serializable
abstract static class Sync extends AbstractQueuedSynchronizer
public Semaphore(int permits)
sync = new NonfairSync(permits);
基本流程
1.建立一个包含n个许可的信号量(内部的计数为n),当一个线程从信号量中请求一个许可(调用acquire()),如果信号量中有许可的话(n大于0),那么线程成功获取许可,信号量内部许可数量减1(n减1);如果信号量中没有许可(n等于0),那么当前线程阻塞。
2.当一个线程归还许可(调用release(),内部计数加1),其他在acquire()方法处等待的线程便有可能被唤醒来竞争许可。
3.公平模式下,如果有线程在acquire()处等待,新来的请求线程会排在这些等待线程后面;非公平模式下,新来的请求线程可能会插队,比在acquire()处等待的线程提前申请到许可。
final int nonfairTryAcquireShared(int acquires)
for (;;)
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
获取一个信号量,然后返回成功,如果获取的时候最终信号量减少为<0了,就直接运行
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
doAcquireSharedInterruptibly
这个方法可以将线程挂起,设置为阻塞状态,等待信号量被释放。
信号量释放的过程
public void release()
sync.releaseShared(1);
释放信号量
public final boolean releaseShared(int arg)
if (tryReleaseShared(arg))
doReleaseShared();
return true;
return false;
调用tryRelease()
protected final boolean tryReleaseShared(int releases)
for (;;)
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
一旦设置成功返回true
然后调用doRelease()方法
* 共享模式下的释放(控制权)动作 -- 唤醒后继节点并保证传递。
* 注:在独占模式下,释放仅仅意味着如果有必要,唤醒头节点的
* 后继节点。
*
*/
private void doReleaseShared()
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
/*
* 保证释放动作(向同步等待队列尾部)传递,即使没有其他正在进行的
* 请求或释放动作。如果头节点的后继节点需要唤醒,那么执行唤
* 动作;如果不需要,将头结点的等待状态设置为PROPAGATE保证
* 唤醒传递。另外,为了防止过程中有新节点进入(队列),这里必
* 需做循环,所以,和其他unparkSuccessor方法使用方式不一样
* 的是,如果(头结点)等待状态设置失败,重新检测。
*/
for (;;)
Node h = head;
//判断同步等待队列是否为空
if (h != null && h != tail)
//如果不为空,获取头节点的等待状态。
int ws = h.waitStatus;
//如果等待状态是SIGNAL,说明其后继节点需要唤醒
//尝试修改等待状态
if (ws == Node.SIGNAL)
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; //如果修改失败,重新循环检测。 // loop to recheck cases
unparkSuccessor(h); //如果修改成功,唤醒头节点的后继节点。
else if (ws == 0 &&//如果等待状态是0,尝试将其(头节点)设置为PROPAGATE
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // 如果设置失败,继续循环检测。 // loop on failed CAS
if (h == head) // 如果过程中头节点没有发生变化,循环退出;否则需要继续检测。 // loop if head changed
break;
非公平的
只要队列中有线程在获取了,那么就认为获取失败,然后等待自己被挂起。
protected int tryAcquireShared(int acquires)
for (;;)
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
CyclicBarrier
public CyclicBarrier(int parties, Runnable barrierAction)
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
/**
* Creates a new <tt>CyclicBarrier</tt> that will trip when the
* given number of parties (threads) are waiting upon it, and
* does not perform a predefined action when the barrier is tripped.
*
* @param parties the number of threads that must invoke @link #await
* before the barrier is tripped
* @throws IllegalArgumentException if @code parties is less than 1
*/
public CyclicBarrier(int parties)
this(parties, null);
其await方法。
public int await() throws InterruptedException, BrokenBarrierException
try
return dowait(false, 0L);
catch (TimeoutException toe)
throw new Error(toe); // cannot happen;
调用dowait方法
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException
final ReentrantLock lock = this.lock;
lock.lock();
try
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted())
breakBarrier();
throw new InterruptedException();
int index = --count;
//下标为0表示当前线程为最后一个使用栅栏的线程。
if (index == 0) // tripped 栅栏开放
boolean ranAction = false;
try
final Runnable command = barrierCommand;
if (command != null) // 如果有栅栏命令,执行栅栏命令
command.run();//看来栅栏的命令是由最后一个到达栅栏的线程执行
ranAction = true;
//产生新的generation。
nextGeneration();
return 0;
finally
if (!ranAction)
breakBarrier();
// 等待中的主循环,直到栅栏开放、栅栏被打破、线程被打断或者超时时退出。
// loop until tripped, broken, interrupted, or timed out
for (;;)
try
if (!timed)
trip.await();// 在trip这个条件上面进行等待
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
catch (InterruptedException ie)
if (g == generation && ! g.broken)
breakBarrier();
throw ie;
else
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L)
breakBarrier();
throw new TimeoutException();
以上是关于Java源码总结锁部分解读的主要内容,如果未能解决你的问题,请参考以下文章