Board logo

标题: Java并发编程-AbstractQueuedSynchronizer源码分析(5) [打印本页]

作者: look_w    时间: 2019-1-17 19:22     标题: Java并发编程-AbstractQueuedSynchronizer源码分析(5)

01    final boolean acquireQueued(final Node node, int arg) {02        boolean failed = true;03        try {04            boolean interrupted = false;05            for (;;) {06                final Node p = node.predecessor();07                if (p == head && tryAcquire(arg)) {08                    setHead(node);09                    p.next = null; // help GC10                    failed = false;11                    return interrupted;12                }13                if (shouldParkAfterFailedAcquire(p, node) &&14                        parkAndCheckInterrupt())15                    interrupted = true;16                    }17        } finally {18            if (failed)19                cancelAcquire(node);20        }21    }[url=][/url]
上述逻辑主要包括:
1. 获取当前节点的前驱节点;
需要获取当前节点的前驱节点,而头结点所对应的含义是当前站有锁且正在运行。
2. 当前驱节点是头结点并且能够获取状态,代表该当前节点占有锁;
如果满足上述条件,那么代表能够占有锁,根据节点对锁占有的含义,设置头结点为当前节点。
3. 否则进入等待状态。
如果没有轮到当前节点运行,那么将当前线程从线程调度器上摘下,也就是进入等待状态。
这里针对acquire做一下总结:
1. 状态的维护;
需要在锁定时,需要维护一个状态(int类型),而对状态的操作是原子和非阻塞的,通过同步器提供的对状态访问的方法对状态进行操纵,并且利用compareAndSet来确保原子性的修改。
2. 状态的获取;
一旦成功的修改了状态,当前线程或者说节点,就被设置为头节点。
3. sync队列的维护。
在获取资源未果的过程中条件不符合的情况下(不该自己,前驱节点不是头节点或者没有获取到资源)进入睡眠状态,停止线程调度器对当前节点线程的调度。
这时引入的一个释放的问题,也就是说使睡眠中的Node或者说线程获得通知的关键,就是前驱节点的通知,而这一个过程就是释放,释放会通知它的后继节点从睡眠中返回准备运行。
下面的流程图基本描述了一次acquire所需要经历的过程:

如上图所示,其中的判定退出队列的条件,判定条件是否满足和休眠当前线程就是完成了自旋spin的过程。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0