上一篇我们讲了AbstractQueuedSynchronizer(一)的大致流程:
AbstractQueuedSynchronizer详解(一)同步器分析
现在来看一下ReentrantLock与他相关的详细源码分析:
目录
(5)、Sync内部类nonfairTryAcquire方法
(9)、 shouldParkAfterFailedAcquire()方法:
(10)、如果挂起失败, 调用parkAndCheckInterrupt()方法
(11)、看一下Thread.interrupted()方法
(4)、NoFairSync内部类hasQueuedPredecessors()
AbstractQueuedSynchronizer的实现,建议结合他的实现类来一起看,这里我们先选择了ReentrantLock可重入独占锁来一起分析:
1、同步器类属性字段:
//同步队列的头节点
private transient volatile Node head;
//同步队列的尾节点
private transient volatile Node tail;
//同步状态
private volatile int state;
//如果超时时间小于此阈值,不阻塞线程,让其自旋,在doAcquireNanos、//doAcquireSharedNanos、awaitNanos、await(long time, TimeUnit unit)
//方法使用到
static final long spinForTimeoutThreshold = 1000L;
//获取UnSafe使用
private static final Unsafe unsafe = Unsafe.getUnsafe();
//属性state的相对偏移量,相对AbstractQueuedSynchronizer实例的起始
//内存位置的相对偏移量,定义成静态的原因是,属性的相对实例的偏移量都是//相等的
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
//内部类Node实例的属性next的相对偏移量
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
//使用UnSafe实例获取AbstractQueuedSynchronizer类的属性
//state的相对偏移量(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
2、同步队列Node对象属性
我们操作的队列,都是基于Node对象的,先来看一下Node对象属性值:
下面来看Node对象的属性:
static final class Node {
//当前节点是获取共享锁的标记
static final Node SHARED = new Node();
//当前节点是获取独占锁的标记
static final Node EXCLUSIVE = null;
//属性waitStatus的值,标志节点对应的线程被取消
static final int CANCELLED = 1;
//属性waitStatus的值,标志当前节点的next节点的线程(即队列中
//当前节点的下一个节点)需要被阻塞,当前节点的后继节点已经 (或即将)
//被阻塞(通过park) , 所以当 当前节点释放或则被取消时候 ,一定要
//unpark它的后继节点。为了避免竞争,获取方法一定要首先设置node
//为signal,然后再次重新调用获取方法,如果失败,则阻塞
static final int SIGNAL = -1;
//属性waitStatus的值,标志当前节点在Condition条件下等待阻塞,
//在Condition实例的await系列方法中使用,新建一个waitStatus的值
//为CONDITION的节点Node,将其加入到Condition中的条件队列中,
//表示当前节点正在条件队列(AQS下的ConditionObject里也维护了个
//队列)中,在从conditionObject队列转移到同步队列前,它不会在同步
//队列(AQS下的队列)中被使用。当成功转移后,该节点的状态值将由
//CONDITION设置为0
static final int CONDITION = -2;
//属性waitStatus的值,标志着下一个acquireShared方法线程应该被允
//许,在获取共享锁
static final int PROPAGATE = -3;
///标记着当前节点的状态,默认状态是0,小于0的状态值都是有特殊作
//用,大于0的状态值表示已取消
volatile int waitStatus;
//使用prev和next实现同步队列,即双向链表,当前节点的前驱节点
volatile Node prev;
//当前节点的下一节点指向当前节点的后继节点,在当前节点释放时候会唤
//醒后继节点。该后继节点也是在入队时候被分配的。当前驱节点被取消时
//候,会重新调整链表的节点链接指向关系。如:前驱节点的前驱节点指向
//当前节点。且把前驱节点设置为null。节点入队操作过程完成前,入队操
//作并还未设置前驱节点的后继节点。所以会看到前驱节点的后继节点为
//null,但是这并不意味着前驱节点就是队列的尾节点!如果后继节点为null,
//我们可以通过从尾节点向前扫描来做双重检测。一个被取消的节点的后继
//节点被设置为自身。即node.next=node。这样设置会帮助
//isOnSyncQueue的执行效率更高(即执行时间更短。注意该方法的if
//(node.next != null))
volatile Node next;
//当前节点对应的线程
volatile Thread thread;
//有两种作用:1、表示下一个在Condition条件上等待的节点,调用
//Condition中await和signal方法,当前节点的线程是拥有独占锁的线
//程2、表示同步队列中的节点是共享模式还是独占模式
//ConditionObject链表的后继节点或者代表共享模式的节点SHARED。
//Condition条件队列:因为Condition队列只能在独占模式下被能被访问。
//我们只需要简单的使用链表队列来链接正在等待条件的节点。再然后它们
//会被转移到同步队列(AQS队列)再次重新获取。由于条件队列只能在
//独占模式下使用,所以我们要表示共享模式的节点的话只要使用特殊值
//SHARED来标明即可。
Node nextWaiter;
//判断当前节点是不是共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
//获取当前节点的前驱节点,如果为null,则抛出空指针异常
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
//返回当前节点的前驱节点
else
return p;
}
//在创建链表头head,或者创建节点共享锁标记属性SHARED值
Node() { }
//在addWaiter方法中使用
Node(Thread thread, Node mode) {
//当前节点的模式,是属于共享模式,还是独占模式
this.nextWaiter = mode;
//将传入进来的线程赋值给节点属性thread
this.thread = thread;
}
//在Condition条件中使用
Node(Thread thread, int waitStatus) { // Used by Condition
//将传入节点的状态值赋值给节点属性waitStatus
this.waitStatus = waitStatus;
//将传入进来的线程赋值给节点属性thread
this.thread = thread;
}
}
3、获取ReentrantLock独占非公平锁
(1)、ReentrantLock 类中的lock方法
/**
* Acquires the lock.
*
* <p>Acquires the lock if it is not held by another thread and returns
* immediately, setting the lock hold count to one.
*
* <p>If the current thread already holds the lock then the hold
* count is incremented by one and the method returns immediately.
*
* <p>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.
*获取锁。
*
* 如果锁没有被另一个线程持有并且返回,则获取锁
* 立即将锁计数器count设置为1。
*
* 如果当前线程已经保持锁定,则锁计数器
* count加1,方法立即返回。(支持重入锁)
*
* 如果锁由另一个线程持有,那么
* 当前线程会被线程调度置于休眠状态,
* 直到获得锁定为止,
* 此时锁计数器count保持设置为1。
* 调用后一直阻塞到获得锁
*/
public void lock() {
sync.lock();
}
(2)、非公平锁的lock()方法
在客户端代码中调用ReentrantLock类的lock()获取锁
//默认非公平锁
//因为非公平性能更好,代码逻辑判断少一些;
Lock lock = new ReentrantLock();
lock.lock();
进入到ReentrantLock类中,发现是调用sync内部类的lock()方法;
public void lock() {
sync.lock();
}
(3)、Sync内部类的lock()方法
abstract void lock();
lock方法实现主要在他的子类NonfairSync和FairSync中
因为这里是非公平锁,所以是调用NonfairSync类的非公平抢占锁方法
final void lock() {
if (compareAndSetState(0, 1))
//成功抢到锁,设置当前线程独占
setExclusiveOwnerThread(Thread.currentThread());
else
//设置状态失败,开始抢占锁
acquire(1);
}
(4)、acquire(1)方法抢占锁
这里调用了父类AbstractQueuedSynchronizer的acquire(1)方法来继续抢占锁
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
首先看if条件中调用的第一个方法,tryAcquire();
父类中的方法tryAcquire()实现没有具体实现,只提供了一个模板方法
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
那就是调用子类中的具体实现
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
(5)、Sync内部类nonfairTryAcquire方法
这里继续调用了Sync内部类nonfairTryAcquire方法
/**
* 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();
//状态值为0的时候,表示未上锁
if (c == 0) {
//cas抢占锁,如果抢占成功,设置独占锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//上述cas抢占失败,或者c状态值不为0的时候
//判断是否为当前线程,如果为当前线程,那么可重入锁+1
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//否则抢占锁失败,返回false
return false;
}
(6)、acquire方法结果
到这里,我们只完成了父类中if条件中的第一个tryAcquire方法:如果tryAcquire抢占成功返回true,!true = false,if第一个条件为false,终止执行;
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
(7)、addWaiter方法
如果tryAcquire抢占失败,那么就要入队列排队等待了;执行acquireQueued()方法,里面还有一个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
//这里快速调用一次入队尾方法,只能插入一个节点,后面的节点都要
//进入 enq()方法插入;
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
//我们再来看enq()方法
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;
// 基于这一步的CAS,如果在插入中,t即tail发生了变化,
//被其他节点先插入到队尾,那么又重试,
//直到所有排队节点线程都插入进去才终止循环
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
(8)、addWaiter()方法执行完毕
经过上面的操作,我们申请获取锁的线程已经成功加入了等待队列,addWaiter()方法执行完毕;
那么节点现在要做的就是挂起当前线程,等待被唤醒;就是下面的 acquireQueued()方法,上述addWaiter()方法执行返回的节点,当做
acquireQueued()的节点参数;
/**
* 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) {
// failed 判断此方法操作是否失败
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 获取前驱节点p,如果p是头结点,那么抢占一次锁
//为什么前驱节点是头结点了,还抢占锁呢?
//如果有非公平锁进来,会抢占锁;
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
// 如果抢占锁成功,则把当前节点设置为头结点
setHead(node);
p.next = null; // help GC
//这里抢占成功以后,把failed标记false,
//方法返回false,这样上一级就不需要调用线程中断方法()
//返回false,表示不是被打断failed = false;
//可以回到调用它的主方法acquire(int arg)中再去查看
return interrupted;
}
// 如果节点线程成功挂起,并且检测到中断,interrupted = true
// 如果成功挂起,没检测到中断,interrupted = false,不中断
// 如果挂起失败,interrupted = false,不中断
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
//取消获取锁
cancelAcquire(node);
}
}
(9)、 shouldParkAfterFailedAcquire()方法:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
// Node.SIGNAL为-1的时候,表示已经挂起了,等待唤醒状态
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
// 大于0,即为1的时候,说明是取消状态
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.
*/
//根据waitStatus的取值限定,这里waitStatus的值
//只能是0或者//PROPAGATE(-3),那么我们把
//前置节点的waitStatus设为Node.SIGNAL(-1)
//然后重新进入调用它的主方法acquireQueued()进行判断
//如果返回true,那就可以挂起了,
//就会进入parkAndCheckInterrupt()方法
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
(10)、如果挂起失败, 调用parkAndCheckInterrupt()方法
private final boolean parkAndCheckInterrupt() {
// 实现阻塞当前线程的功能,LockSupport底层
//添加了blocker对象,该对象主要用于问题排查和系统监控,
//会打印更多的 dump //堆栈信息,并且不会像Thread中
// resume()和suspend()那样:
//如果 resume() 操作出现在 suspend() 之前执行,
//那么线程将一直处于挂起状态,同时一直占用锁,这就产生了死锁
LockSupport.park(this);
//被唤醒之后,返回中断标记,即如果是正常唤醒返回false,
//如果是中断醒来,就返回true
return Thread.interrupted();
}
(11)、看一下Thread.interrupted()方法
/**
* Tests whether the current thread has been interrupted. The
* <i>interrupted status</i> of the thread is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return false (unless the current thread were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if the current thread has been interrupted;
* <code>false</code> otherwise.
* @see #isInterrupted()
* @revised 6.0
*/
//返回当前线程是否中断,如果中断返回true;
//此方法会清除线程的中断状态,
//换句话说:如果这个方法连续两次被调用,那么
//第二次调用将返回false(除非当前线程是
//在第一次通话清除其中断状态后,在第二次检查中断之前,
//再次进行了中断)
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
(12)、回到主方法acquire()查看:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
如果先获取锁成功,if第一个条件返回false,!flase =true,后续不执行;
如果先获取锁失败,!flase = true,并且acquireQueued()加入队列成功,当前线程打断selfInterrupt(),让出cpu资源,等待队列唤醒;
如果先获取锁失败,!flase = true,并且acquireQueued()方法返回失败(抢占锁成功则入队失败),则不打断当前线程:
(13)、获取锁方法总结
至此,一个独占非公平锁的获取流程就执行完成了,上述是解析ReentrantLock的lock()方法获取锁的流程;
在客户调用调用ReentrantLock的trylock()方法也是调用Sync内部类的nonfairTryAcquire()方法;
lock()方法:调用后一直阻塞到获得锁,等待唤醒获取锁;
trylock()方法与lock()方法的区别是:尝试是否能获得锁,如果不能获得立即返回;
ReentrantLock的lockInterruptibly()方法:调用后一直阻塞到获得锁但是接受中断信号;
4、获取ReentrantLock独占公平锁
(1)、客户端使用ReentrantLock:
Lock lock = new ReentrantLock(true);
lock.lock();
来看构造方法,这里会用公平内部类FairSync:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
(2)、调用FairSync公平锁内部类中的lock方法
final void lock() {
acquire(1);
}
这里的acquire(1)是调用了父类AbstractQueuedSynchronizer的方法
/**
* 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}.
*
* @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();
}
(3)、FairSync实现的tryAcquire()
跟NonFairSync类似,tryAcquire()在父类中只是一个模板放,首先是调用子类FairSync实现的tryAcquire() 方法尝试获取锁资源,如果成功则整个acquire()方法执行完毕,即当前线程获得锁资源,可以进入临界区。
我们来看FairSync实现的tryAcquire() 方法,
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;
}
}
(4)、NoFairSync内部类hasQueuedPredecessors()
与NoFairSync内部类中tryAcquire()方法唯一的不同是,if条件中多了一个 !hasQueuedPredecessors()判断,这个方式是干嘛的呢?
* @return {@code true} if there is a queued thread preceding the
* current thread, and {@code false} if the current thread
* is at the head of the queue or the queue is empty
* @since 1.7
*是否有前继结点
*/
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
// 头尾节不相等(说明队列不止一个节点);
// 并且当前线程节点,不等于头结点的下一个节点;
//因为是头节点持有锁,头结点释放锁以后,会通知它的next节点线程
//去获得锁;这里判断前任节点是否为头结点的下一个节点,
//当前节点线程就不去和头结点的next节点线程抢占锁;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
这就是公平的体现,查看释放有前驱节点抢占锁,hasQueuedPredecessors如果返回false,说明没有前驱节点,!false=true,在tryAcquire ()方法中,if的第一个条件为true之后,才会执行下面的抢占锁流程;
获取公平锁的流程,剩下的和前面非公平锁完全一样了。
5、tryLock()方法,尝试获取锁
除了调用lock()方法,还有tryLock()方法可以尝试获取锁
(1)、客户端代码
Lock lock = new ReentrantLock();
lock.tryLock();
到源码中:
/**
* Acquires the lock only if it is not held by another thread at the time
* of invocation.
*
* <p>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()} <em>will</em>
* 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).
*
* <p>If the current thread already holds this lock then the hold
* count is incremented by one and the method returns {@code true}.
*
* <p>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
*/
//使用公平策略时,调用tryLock()将立即尝试获取锁,
//无论其他线程是否当前正在等待锁。
//这种“闯入”行为在某些情形下会有用,
//尽管他会打破公平策略的公平性,
//如果你想此锁的公平性设置,请使用
//带时间戳的tryLock(long,TimeUnit)方法
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
所以公平锁使用tryLock()会导致不公平;需要使用tryLock(long,TimeUnit);
6、释放锁
前面讲了非公平锁与公平锁获取逻辑,下面讲一下释放锁的流程:
释放锁的时候,不区分公平与非公平之分;
(1)、unlock()方法
在客户端调用unlock()方法
Lock lock = new ReentrantLock(true);
lock.lock();
lock.unlock();
Lock接口中也定义了lock()方法
void unlock();
实现在ReentrantLock中
public void unlock() {
sync.release(1);
}
(2)、父类AQS中release()方法:
/**
* Releases in exclusive mode. Implemented by unblocking one or
* more threads if {@link #tryRelease} returns true.
* This method can be used to implement method {@link Lock#unlock}.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryRelease} but is otherwise uninterpreted and
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
public final boolean release(int arg) {
//tryRealease返回true以后,说明重入锁都释放完
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//开始唤醒后驱节点
unparkSuccessor(h);
return true;
}
return false;
}
父类中的tryRealease()也是一个模板方法,显然需要子类去实现
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
(3)、子类Sync类中tryRelease()方法
(在FairSync和NonFairSync中都没有重新此方法,说明公平和非公平锁,都调用的这同一个释放重入锁的方法)
//可重入锁计数器-1,知道计数器=0的时候,把独占线程置空,返回true;
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;
}
(4)、 unparkSuccessor()唤醒方法
/**
* Wakes up node's successor, if one exists.
*
* @param node the node
*/
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) {
s = null;
//直到找到前面为非取消状态的节点,然后执行下面的唤醒unpark
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
//直接调用LockSupport里面的唤醒线程方法
LockSupport.unpark(s.thread);
}
至此,锁的释放已经完成!
下一篇: