java aqs源码_java中AQS源码分析

AQS内部采用CLH队列。CLH队列是由节点组成。内部的Node节点包含的状态有

static final int CANCELLED =  1;

static final int SIGNAL    = -1;

static final int CONDITION = -2;

static final int PROPAGATE = -3;

其中取消状态表示任务的取消,SIGNAL状态表示后续节点需要唤醒,CONDITION表示等待状态,PROPAGRATE表示的是传播状态通常用于共享锁的释放。初始节点的状态为0。

AQS中比较重要的操作包括:

public final void acquire(int arg);

public final void acquireInterruptibly(int arg);

public final void acquireShared(int arg);

public final void acquireSharedInterruptibly(int arg);

protected boolean tryAcquire(int arg);

protected int tryAcquireShared(int arg);

public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException;

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException;

其中:public final void acquire(int arg) {

if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

selfInterrupt();

}

其中tryAcquire方法为抽象方法。不同的子类有不同的实现方式。AQS中该方法的实现知识抛出了一个异常。

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);//拿到资源后,将head指向该结点。所以head所指的标杆结点,就是当前获取到资源的那个结点或null。

p.next = null; // setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!

failed = false;

return interrupted;//返回等待过程中是否被中断过

}

//如果自己可以休息了,就进入waiting状态,直到被unpark()

if (shouldParkAfterFailedAcquire(p, node) &&

parkAndCheckInterrupt())

interrupted = true;//如果等待过程中被中断过,哪怕只有那么一次,就将interrupted标记为true

}

} finally {

if (failed)

cancelAcquire(node);

}

}

可以从该方法中看出。这里会继续尝试去获取一下资源

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

int ws = pred.waitStatus;//拿到前驱的状态

if (ws == Node.SIGNAL)

//如果已经告诉前驱拿完号后通知自己一下,那就可以安心休息了

return true;

if (ws > 0) {

/*

* 如果前驱放弃了,那就一直往前找,直到找到最近一个正常等待的状态,并排在它的后边。

* 注意:那些放弃的结点,由于被自己“加塞”到它们前边,它们相当于形成一个无引用链,稍后就会被保安大叔赶走了(GC回收)!

*/

do {

node.prev = pred = pred.prev;

} while (pred.waitStatus > 0);

pred.next = node;

} else {

//如果前驱正常,那就把前驱的状态设置成SIGNAL,告诉它拿完号后通知自己一下。有可能失败,人家说不定刚刚释放完呢!

compareAndSetWaitStatus(pred, ws, Node.SIGNAL);

}

return false;

}

在该方法中会检测当前节点中前面的节点是否有CANCELLED状态的如果有。待会儿后续的操作这些节点会被GC回收。

如果一切正常当前节点的前一个节点会被设置为SIGNAL状态。

1 private final boolean parkAndCheckInterrupt() {

2     LockSupport.park(this);//调用park()使线程进入waiting状态

3     return Thread.interrupted();//如果被唤醒,查看自己是不是被中断的。

4 }

线程进入waiting状态。线程被唤醒的方式有被unpark和被中断。

public final boolean release(int arg) {

if (tryRelease(arg)) {

Node h = head;//找到头结点

if (h != null && h.waitStatus != 0)

unparkSuccessor(h);//唤醒等待队列里的下一个线程

return true;

}

return false;

}

这里tryRelease也是一个抽象方法不同的子类有不同的实现。

private void unparkSuccessor(Node node) 内部首先会设置当前节点的状态为初始状态。同时找到一个有效的后继节点的状态小于0的进行节点的释放。 LockSupport.unpark(s.thread);//唤醒对应的线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值