队列同步器是用来构建锁或者其他同步组件的框架,使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
子类通过集成同步器并实现它的抽象方法来管理同步状态。同步器是锁的底层实现者。
减少上下文切换
下面先复习下如何减少上下文切换:
1. 无锁并发编程;
2. CAS算法
3. 使用最少线程
4. 使用协程,在单线程里实现多个任务的调度,并在单线程里维持多个任务间的切换。
避免死锁的方法
1.避免同一个线程同时获取多个锁;
2. 避免一个线程在锁内同时占有多个资源;
3. 尝试使用定时锁
队列同步器
AbstractQueuedSynchronizer –使用者必须继承并重写指定的方法,随后将同步器组合在自定义同步组件中的实现中,并调用同步器提供的模板方法,而这些模板方法会调用使用者重写的方法。
同步器提供的模板方法分为3类:独占式获取与释放同步状态;共享式获取与释放同步状态;查询同步队列中等待线程的情况。下面是实现的具体分析过程:
同步队列
首先看下同步队列。同步器依赖一个同步队列(FIFO的双向队列)来完成同步状态的管理。
过程:当前线程获取同步状态失败后,同步器会将当前线程以及等待状态等信息构造成一个节点并且加入同步队列,同时会阻塞当前的线程,当同步状态释放时,会把首节点的线程唤醒,使其再次尝试获取同步状态。
下面是一些具体的注意细节:
1. 在将一个节点加入队尾的时候,可能有多个线程竞争,这是同步器提供了一个基于CAS的设置尾节点的方法:compareAndSetTail(Node expect, Node updata).
2. 每次获得同步状态的节点都是首节点,而且当首节点在释放同步状态的时候回唤醒后继节点,而后继节点在成果获取同步状态之后时将自己设置尾首节点。(设置首节点的时候不需要保证线程安全,因为就一个线程能够设置首节点)
独占式同步状态的获取与释放
通过调用同步器的acquire(int args)方法可以获取同步状态,当一个线程进入同步队列后,后续对线程进行中断操作,线程不会从同步队列中移除,
独占式同步状态的获取主要有几个步骤:
1. 同步状态的获取 (tryAcquire( args)方法)
2. 节点构造
3. 加入同步队列
4. 同步队列中自旋等待
具体的部分实现细节:
1. 在构造好节点加入同步队列的时候,采用了以下策略:
首先不使用CAS尝试快速的将节点加入尾部(成功的话结束(需要一次compareAndSetTail判断));
否则使用CAS将节点设置成尾节点。
2. 节点在队列中时候的自旋
当一个节点 在队列中的时候,都在自省的观察条件(前驱节点是否为首节点,只有首节点才能尝试获取同步状态(因为只有首节点才能成功获取同步状态,并且需要满足fifo特性))是否满足,满足则获取同步状态,然后从自旋中退出。
独占式同步状态的释放逻辑
通过调用release(int args)来释放同步状态,释放同步状态后会唤醒后继(unparkSuccessor(Node node))节点。
共享式和独占式同步状态的获取与释放
共享式:所谓共享式同步状态是指在同一个时刻是否有多个线程获取同步状态。
独占式超时:与独占式类似,不过会使当前线程等待一定的时间,如果当前线程在一定时间内没有获得同步状态,将会从等待逻辑中自动返回。
下一节将分析java中几种同步工具,可重入锁等。