AQS源码
Posted 沸羊羊一个
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AQS源码相关的知识,希望对你有一定的参考价值。
import java.util.concurrent.TimeUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import sun.misc.Unsafe;
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable
private static final long serialVersionUID = 7373984972572414691L;
protected AbstractQueuedSynchronizer()
static final class Node
/** 标记表示一个节点在分享模式下等待 */
static final Node SHARED = new Node();
/**标记表示一个节点在独占模式下等待 */
static final Node EXCLUSIVE = null;
/** 表示取消线程的值 */
static final int CANCELLED = 1;
/** 信号值,表示需要unparking(唤醒线程)的信息 */
static final int SIGNAL = -1;
/** 表示线程等待条件的值 */
static final int CONDITION = -2;
/**
* 表示获取下一个共享
* 无条件的传播(用于一级级释放资源)
*/
static final int PROPAGATE = -3;
//等待状态(volatile修饰,可见性、原子性)
volatile int waitStatus;
//上一个节点
volatile Node prev;
//下一个节点
volatile Node next;
//节点入队列时的线程。初始化时构建,使用完成后清空
volatile Thread thread;
//下一个等待的节点
Node nextWaiter;
/**
* 如果节点在共享模式中等待,则返回true
*/
final boolean isShared()
return nextWaiter == SHARED;
//返回上一个节点,如果上一个节点为空则抛出异常
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;
/**
* 等待队列的头结点(懒加载方式实例化),除初始化外,只能使用setHead方法改变其值。
* 如果存在,它的状态保证不能取消
*/
//头节点
private transient volatile Node head;
/**
* 等待队列的尾节点(懒加载方式实例化),只能通过尾部添加节点方式修改
*/
//尾节点
private transient volatile Node tail;
/**
* 同步状态
*/
private volatile int state;
/**
* 获取当前同步状态值
*/
protected final int getState()
return state;
/**
* 设置同步状态值
*/
protected final void setState(int newState)
state = newState;
/**
* 比较并设置同步状态值(此操作具备原子性,可见行 读写操作)
* 比较:设置前比较当前的实际值和期望值是否一致,一致说明没有其它线程获得锁,则设置update值。
* 如果不一致,说明其它线程获得锁,正在修改值,则当前线程放弃更新值。
* @param expect 期望值
* @param update 要更新得新值
* @return 设置成功返回true. 当前的实际值和期望值不一致,当前线程放弃更新,返回false
*/
protected final boolean compareAndSetState(int expect, int update)
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
// Queuing utilities
/**
* The number of nanoseconds for which it is faster to spin
* rather than to use timed park. A rough estimate suffices
* to improve responsiveness with very short timeouts.
*/
static final long spinForTimeoutThreshold = 1000L;
/**
* 将节点插入到队列中,必要时进行初始化。
* @param node 新插入的节点
* @return 上一个节点
*/
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,不管前一步有多少新节点都指向了尾节点,这一步只有一个能真正入队成功,其他的都必须重新执行循环体
if (compareAndSetTail(t, node))
t.next = node;
return t;//该循环体唯一退出的操作,就是入队成功(否则就要无限重试)
/**
* 在当前线程给定的模式下创建节点
* @param 要创建的节点 模式: Node.EXCLUSIVE(独占模式), Node.SHARED(分享模式)
* @return 返回新创建的节点
*/
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的逻辑,即使并发操作也只能有一个线程成功并返回,其余的都要执行后面的入队操作。即enq()方法
if (compareAndSetTail(pred, node)) //新创建的节点设置为队列的尾部节点
pred.next = node;
return node;
enq(node);//入队操作
return node;
/**
* 设置队列头部节点
*/
private void setHead(Node node)
head = node;
node.thread = null;
node.prev = null;
/**
* 唤醒后继节点(如果存在)
* @param node the node
*/
private void unparkSuccessor(Node node)
/**
* waitStatus(节点状态对应的值)
* CANCELLED:1
* SIGNAL :-1
* CONDITION :-2
* PROPAGATE :-3
* 状态0 初始化状态,也代表正在尝试去获取临界资源的线程所对应的Node的状态。
*/
int ws = node.waitStatus;//节点同步状态
if (ws < 0)
//把标记为设置为0,表示唤醒操作已经开始进行,提高并发环境下性能
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
//如果当前节点的后继节点为null,或者已经被取消
if (s == null || s.waitStatus > 0)
s = null;
//注意这个循环没有break,也就是说它是从后往前找,一直找到离当前节点最近的一个等待唤醒的节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
//执行唤醒操作
if (s != null)
LockSupport.unpark(s.thread);//唤醒离当前节点最近的一个等待唤醒的节点
/**
* 共享模式的释放(唤醒)操作
*/
private void doReleaseShared()
//无限循环
for (;;)
//唤醒操作由头结点开始,注意这里的头节点已经是上面新设置的头结点了
//其实就是唤醒上面新获取到共享锁的节点的后继节点
Node h = head;
if (h != null && h != tail) //节点不为空而且不是尾部节点
int ws = h.waitStatus;//获取节点的同步状态
//表示后继节点需要被唤醒
if (ws == Node.SIGNAL)
//这里需要控制并发,因为入口有setHeadAndPropagate跟release两个,避免两次unpark
//cvs 把标记为设置为0,表示唤醒操作已经开始进行,提高并发环境下性能
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//执行唤醒操作
unparkSuccessor(h);
//如果后继节点暂时不需要唤醒,则把当前节点状态设置为PROPAGATE确保以后可以传递下去
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
//如果头结点发生变化,比如说其他线程获取到了锁,为了使自己的唤醒动作可以传递,必须进行重试
if (h == head) //如果头结点没有发生变化,表示设置完成,退出循环
break;
/**
* 共享锁模式获取成功以后,调用此方法设置新的头结点以外还有一个传递动作
* @param 当前成功获取共享锁的节点
* @param tryAcquireShared方法的返回值,注意:它可能大于0也可能等于0
*/
private void setHeadAndPropagate(Node node, int propagate)
Node h = head; // 要检查的老头部节点
//设置新的头节点,即把当前获取到锁的节点设置为头节点
//注:这里是获取到锁之后的操作,不需要并发控制
setHead(node);
//这里意思有两种情况是需要执行唤醒操作
//1.propagate > 0 表示调用方指明了后继节点需要被唤醒
//2.头节点后面的节点需要被唤醒(waitStatus<0),不论是老的头结点还是新的头结点
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0)
Node s = node.next;
//如果当前节点的后继节点是共享类型获取没有后继节点,则进行唤醒
//这里可以理解为除非明确指明不需要唤醒(后继等待节点是独占类型),否则都要唤醒
if (s == null || s.isShared())
doReleaseShared();
/**
* 取消正在进行尝试获取的锁(获取失败节点的处理)
* 其实能进入这里的就是tryAcquire()方法抛出异常,也就是说AQS框架针对开发人员自己实现的获取锁操作如果抛出异常,也做了妥善的处理
* @param node : 当前获取锁资源失败的节点
*/
private void cancelAcquire(Node node)
//如果节点不存在则直接忽略
if (node == null)
return;
node.thread = null;
// 跳过所有已经取消的前置节点,跟上面的那段跳转逻辑类似
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
//这个是前置节点的后继节点,由于上面可能的跳节点的操作,所以这里可不一定就是当前节点
Node predNext = pred.next;
//把当前节点waitStatus置为取消,这样别的节点在处理时就会跳过该节点
node.waitStatus = Node.CANCELLED;
//如果当前是尾节点,则直接删除,即出队
//注:这里不用关心CAS失败,因为即使并发导致失败,该节点也已经被成功删除
if (node == tail && compareAndSetTail(node, pred))
compareAndSetNext(pred, predNext, null);
else
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null)
Node next = node.next;
if (next != null && next.waitStatus <= 0)
//这里的判断逻辑很绕,具体就是如果当前节点的前置节点不是头节点且它后面的节点等待它唤醒(waitStatus小于0),
//再加上如果当前节点的后继节点没有被取消就把前置节点跟后置节点进行连接,相当于删除了当前节点
compareAndSetNext(pred, predNext, next);
else
//进入这里,要么当前节点的前置节点是头结点,要么前置节点的waitStatus是PROPAGATE,直接唤醒当前节点的后继节点
unparkSuccessor(node);
node.next = node; // help GC
/**
* 检查和更新未能获取的节点的状态。
* @param pred 前置节点
* @param node 当前线程节点
* @return 如果线程阻塞,则返回true
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)
int ws = pred.waitStatus;//获取前置节点状态
if (ws == Node.SIGNAL)
//如果前置节点的waitStatus是Node.SIGNAL则返回true,然后会执行parkAndCheckInterrupt()方法进行挂起
return true;
if (ws > 0)
//由waitStatus的几个取值可以判断这里表示前置节点被取消
do
node.prev = pred = pred.prev;
while (pred.waitStatus > 0);
//这里我们由当前节点的前置节点开始,一直向前找最近的一个没有被取消的节点
//注,由于头结点head是通过new Node()创建,它的waitStatus为0,因此这里不会出现空指针问题,也就是说最多就是找到头节点上面的循环就退出了
pred.next = node;
else
//根据waitStatus的取值限定,这里waitStatus的值只能是0或者PROPAGATE,那么我们把前置节点的waitStatus设为Node.SIGNAL然后重新进入该方法进行判断
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
return false;
/**
* 中断当前线程
*/
static void selfInterrupt()
Thread.currentThread().interrupt();
/**
* 线程进入等待,然后检查是否中断唤醒。
* @return 中断唤醒,就返回true
*/
private final boolean parkAndCheckInterrupt()
//调用park()使线程进入waiting状态
LockSupport.park(this);
//被唤醒之后,返回中断标记,即如果是正常唤醒则返回false,如果是由于中断醒来,就返回true
return Thread.interrupted();
/**
* 使线程在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
* @param node the node
* @param arg 获得资源的参数
* @return 如果在整个等待过程中被中断过,则返回true,否则返回false
*/
final boolean acquireQueued(final Node node, int arg)
//锁资源获取失败标记位
boolean failed = true;
try
//等待线程被中断标记位
boolean interrupted = false;
//这个循环体执行的时机包括新节点入队和队列中等待节点被唤醒两个地方
for (;;)
//获取当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点就是头结点,则尝试获取锁资源
//如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。
if (p == head && tryAcquire(arg))
//当前节点获得锁资源以后设置为头节点,这里继续理解我上面说的那句话
//头结点就表示当前正占有锁资源的节点
setHead(node);
// setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!
p.next = null; // help GC
//表示锁资源成功获取,因此把failed置为false
failed = false;
//返回中断标记,表示当前节点是被正常唤醒还是被中断唤醒
return interrupted;
//如果没有获取锁成功(检查和更新未能获取的节点的状态),如果线程阻塞则进入挂起逻辑
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
finally
if (failed)
//最后会分析获取锁失败处理逻辑
cancelAcquire(node);
/**
* 大体上相当于前面的acquireQueued,关键的区别在于检测到interrupted后的处理,
* acquireQueued简单的记录下中断曾经发生,然后继续去尝试获取锁,失败则休眠。
* 而doAcquireInterruptibly检测到中断则直接退出循环,抛出InterruptedException异常
*
* @param arg 获得资源的参数
*/
private void doAcquireInterruptibly(int arg)
throws InterruptedException
//在当前线程创建一个独占模式节点
final Node node = addWaiter(Node.EXCLUSIVE);
//锁资源获取失败标记位
boolean failed = true;
try
for (;;)
//获取当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点就是头结点,则尝试获取锁资源
//如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。
if (p == head && tryAcquire(arg))
//头结点就表示当前正占有锁资源的节点
setHead(node);
// setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!
p.next = null; // help GC
//表示锁资源成功获取,因此把failed置为false
failed = false;
return;
//检查和更新未能获取的节点的状态。如果线程阻塞则进入挂起逻辑
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();//抛出异常
finally
if (failed)
//最后会分析获取锁失败处理逻辑
cancelAcquire(node);
/**
* 在限定时间内获取独占锁
*
* @param arg 获取资源参数
* @param nanosTimeout 最大的限定事件
* @return 获取成功返回true
*/
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException
if (nanosTimeout <= 0L)//时间小于等于0 ,直接返回false,没有获得锁
return false;
//计算最后期限
final long deadline = System.nanoTime() + nanosTimeout;
//创建独占锁节点,加入队列尾部
final Node node = addWaiter(Node.EXCLUSIVE);
//锁资源获取失败标记位
boolean failed = true;
try
for (;;)
//获取当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点就是头结点,则尝试获取锁资源
//如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。
if (p == head && tryAcquire(arg))
//头结点就表示当前正占有锁资源的节点
setHead(node);
// setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!
p.next = null; // help GC
//表示锁资源成功获取,因此把failed置为false
failed = false;
return true;//直接返回获取锁
//计算剩余时间
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)//超时则不再尝试获取锁,直接返回false
return false;
//检查和更新未能获取的节点的状态。如果线程阻塞则 比较纳秒时间
//如果没有超时,则等待nanosTimeout纳秒
//注:该线程会直接从LockSupport.parkNanos中返回
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold) //spinForTimeoutThreshold 提高很短的超时的响应
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())//如果当前线程被中断则泡异常
throw new InterruptedException();
finally
if (failed)
//最后会分析获取锁失败处理逻辑
cancelAcquire(node);
/**
* 用于将当前线程加入等待队列尾部休息,直到其他线程释放资源唤醒自己,自己成功拿到相应量的资源后才返回
* @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();
//如果前置节点就是头结点,则尝试获取锁资源,如果到head的下一个,因为head是拿到资源的线程,此时node被唤醒,很可能是head用完资源来唤醒自己的
if (p == head)
//如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。
int r = tryAcquireShared(arg);
if (r >= 0)
setHeadAndPropagate(node, r);//将head指向自己,还有剩余资源可以再唤醒之后的线程
// setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!
p.next = null; // help GC
if (interrupted)//如果等待过程中被打断过,此时将中断补上。
selfInterrupt();
failed = false;
return;
//判断状态,寻找安全点,进入waiting状态,等着被unpark()或interrupt()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
finally
if (failed)
//最后会分析获取锁失败处理逻辑
cancelAcquire(node);
/**
* 在可中断模式下获取资源
* @param arg the acquire argument
*/
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException
//创建共享节点,加入队列尾部
final Node node = addWaiter(Node.SHARED);
//锁资源获取失败标记位
boolean failed = true;
try
for (;;)
//获取当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点就是头结点,则尝试获取锁资源,如果到head的下一个,因为head是拿到资源的线程,此时node被唤醒,很可能是head用完资源来唤醒自己的
if (p == head)
//如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。
int r = tryAcquireShared(arg);
if (r >= 0)
//将head指向自己,还有剩余资源可以再唤醒之后的线程
setHeadAndPropagate(node, r);
// setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!
p.next = null; // help GC
//表示锁资源成功获取,因此把failed置为false
failed = false;
return;
//判断状态,寻找安全点,进入waiting状态,等着被unpark()或interrupt()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
finally
if (failed)
//最后会分析获取锁失败处理逻辑
cancelAcquire(node);
/**
* 用于在一定时间范围内 将当前线程加入等待队列尾部休息,直到其他线程释放资源唤醒自己,自己成功拿到相应量的资源后才返回
* 和doAcquireShared差不多
* @param arg the acquire argument
* @param nanosTimeout 等待的最长时间
* @return @code true if acquired
*/
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException
if (nanosTimeout <= 0L)
return false;
//计算最后期限
final long deadline = System.nanoTime() + nanosTimeout;
//创建共享节点,加入队列尾部
final Node node = addWaiter(Node.SHARED);
//锁资源获取失败标记位
boolean failed = true;
try
for (;;)
//获取当前节点的前置节点
final Node p = node.predecessor();
//如果前置节点就是头结点,则尝试获取锁资源,如果到head的下一个,因为head是拿到资源的线程,此时node被唤醒,很可能是head用完资源来唤醒自己的
if (p == head)
//如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。
int r = tryAcquireShared(arg);
if (r >= 0)
//将head指向自己,还有剩余资源可以再唤醒之后的线程
setHeadAndPropagate(node, r);
// setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!
p.next = null; // help GC
//表示锁资源成功获取,因此把failed置为false
failed = false;
return true;//返回true,表示已获得锁
//计算剩余时间
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
//检查和更新未能获取的节点的状态。如果线程阻塞则 比较纳秒时间
//如果没有超时,则等待nanosTimeout纳秒
//注:该线程会直接从LockSupport.parkNanos中返回
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold) //spinForTimeoutThreshold 提高很短的超时的响应
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())//如果当前线程被中断则泡异常
throw new InterruptedException();
finally
if (failed)
//最后会分析获取锁失败处理逻辑
cancelAcquire(node);
/**************************************************/
//什么?直接throw异常?说好的功能呢?好吧,还记得概述里讲的AQS只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现吗?就是这里了!!!
// AQS这里只定义了一个接口,具体资源的获取交由自定义同步器去实现了(通过state的get/set/CAS)!!!至于能不能重入,能不能加塞,
// 那就看具体的自定义同步器怎么去设计了!!!当然,自定义同步器在进行资源访问时要考虑线程安全的影响。
//这里之所以没有定义成abstract,是因为独占模式下只用实现tryAcquire-tryRelease,
// 而共享模式下只用实现tryAcquireShared-tryReleaseShared。如果都定义成abstract,那么每个模式也要去实现另一模式下的接口。
// 说到底,Doug Lea还是站在咱们开发者的角度,尽量减少不必要的工作量。
/************************************************/
/**
* 尝试去获取独占资源。如果获取成功,则直接返回true,否则直接返回false
*/
protected boolean tryAcquire(int arg)
throw new UnsupportedOperationException();
/**
* 独占方式。尝试释放资源,成功则返回true,失败则返回false
*/
protected boolean tryRelease(int arg)
throw new UnsupportedOperationException();
/**
* 共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源
*/
protected int tryAcquireShared(int arg)
throw new UnsupportedOperationException();
/**
共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false
*/
protected boolean tryReleaseShared(int arg)
throw new UnsupportedOperationException();
/**
该线程是否正在独占资源。只有用到condition才需要去实现它
*/
protected boolean isHeldExclusively()
throw new UnsupportedOperationException();
/**
此方法是独占模式下线程获取共享资源的顶层入口。如果获取到资源,线程直接返回,
否则进入等待队列,直到获取到资源为止,且整个过程忽略中断的影响。这也正是lock()的语义,当然不仅仅只限于lock()。
获取到资源后,线程就可以去执行其临界区代码了
*/
public final void acquire(int arg)
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
/**
* 独占模式获取锁,如果在获取锁的过程中线程被中断,则直接抛出中断异常
*/
public final void acquireInterruptibly(int arg)
throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
/**
尝试在指定纳秒时间内获取独占锁,如果被中断则抛出中断异常
*/
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
/**
释放锁资源
*/
public final boolean release(int arg)
if (tryRelease(arg))
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
return false;
/**
共享模式获取锁,不响应中断,如果发生中断只会把当前线程的中断状态设置为true
*/
public final void acquireShared(int arg)
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
/**
共享模式获取锁,如果在获取锁的过程中线程被中断,则直接抛出中断异常
*/
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
/**
尝试在指定纳秒时间内获取共享锁,如果被中断则抛出中断异常
*/
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
/**
释放锁资源
*/
public final boolean releaseShared(int arg)
if (tryReleaseShared(arg))
doReleaseShared();
return true;
return false;
// Queue inspection methods
/**
查询是否有线程正在等待获取指定的对象监视器
*/
public final boolean hasQueuedThreads()
return head != tail;
/**
查询队列是否被多个acquire请求竞争过(导致某个线程阻塞过)。为什么head 不为null就能证明?有竞争就会入队列此时head不为null,但是任务执行完了呢?
通过上面的代码知道,head是由队列里刚获得到锁的线程设置的
(把自己设置成head),即使任务执行完也不会修改head,只能由下个入队的线程设置,这样head就永远不会为空了。
*/
public final boolean hasContended()
return head != null;
/**
返回队列里第一个没有获取到锁的线程,如果head等于tail说明队列里没有线程在等待,直接返回null;否则,调用fullGetFirstQueuedThread。
*/
public final Thread getFirstQueuedThread()
// handle only fast path, else relay
return (head == tail) ? null : fullGetFirstQueuedThread();
/**
* Version of getFirstQueuedThread called when fastpath fails
*/
private Thread fullGetFirstQueuedThread()
/*
* The first node is normally head.next. Try to get its
* thread field, ensuring consistent reads: If thread
* field is nulled out or s.prev is no longer head, then
* some other thread(s) concurrently performed setHead in
* between some of our reads. We try this twice before
* resorting to traversal.
*/
// 一般来说这个线程就是head->next,需要保证在并发情况下读一致性:
// 1. (h = head) != null
// 2. (s = h.next) != null
// 3. s.prev = head
// 4. (st = s.thread) != null
// 假设2、3中间被并发的插入了一个setHead方法,执行3时发现s.prev为空了
// 因此,这里需要再试一次(第二次其实也有可能会失败,不过概率已经很小
// 了,就像连续被雷劈两次一样;即使第二次也失败了,还有后面最后一道安
// 全措施,从tail开始向前遍历寻找)。
Node h, s;
Thread st;
if (((h = head以上是关于AQS源码的主要内容,如果未能解决你的问题,请参考以下文章