现在的位置: 首页 > 综合 > 正文

AbstractQueuedSynchronizer的介绍和原理分析

2013年02月20日 ⁄ 综合 ⁄ 共 2144字 ⁄ 字号 评论关闭

简介

提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步装置的基础框架。该同步器(以下简称同步器)利用了一个int来表示状态,期望它能够成为实现大部分同步需求的基础。使用的方法是继承,子类通过继承同步器并需要实现它的方法来管理其状态,管理的方式就是通过类似acquire和release的方式来操纵状态。然而多线程环境中对状态的操纵必须确保原子性,因此子类对于状态的把握,需要使用这个同步器提供的以下三个方法对状态进行操作:

  • java.util.concurrent.locks.AbstractQueuedSynchronizer.getState()
  • java.util.concurrent.locks.AbstractQueuedSynchronizer.setState(int)
  • java.util.concurrent.locks.AbstractQueuedSynchronizer.compareAndSetState(int, int)

子类推荐被定义为自定义同步装置的内部类,同步器自身没有实现任何同步接口,它仅仅是定义了若干acquire之类的方法来供使用。该同步器即可以作为排他模式也可以作为共享模式,当它被定义为一个排他模式时,其他线程对其的获取就被阻止,而共享模式对于多个线程获取都可以成功。


同步器是实现锁的关键,利用同步器将锁的语义实现,然后在锁的实现中聚合同步器。可以这样理解:锁的API是面向使用者的,它定义了与锁交互的公共行为,而每个锁需要完成特定的操作也是透过这些行为来完成的(比如:可以允许两个线程进行加锁,排除两个以上的线程),但是实现是依托给同步器来完成;同步器面向的是线程访问和资源控制,它定义了线程对资源是否能够获取以及线程的排队等操作。锁和同步器很好的隔离了二者所需要关注的领域,严格意义上讲,同步器可以适用于除了锁以外的其他同步设施上(包括锁)。
同步器的开始提到了其实现依赖于一个FIFO队列,那么队列中的元素Node就是保存着线程引用和线程状态的容器,每个线程对同步器的访问,都可以看做是队列中的一个节点。Node的主要包含以下成员变量:

1 Node
{
2     int waitStatus;
3     Node
prev;
4     Node
next;
5     Node
nextWaiter;
6     Thread
thread;
7 }

以上五个成员变量主要负责保存该节点的线程引用,同步等待队列(以下简称sync队列)的前驱和后继节点,同时也包括了同步状态。

属性名称 描述
int waitStatus 表示节点的状态。其中包含的状态有:

  1. CANCELLED,值为1,表示当前的线程被取消;
  2. SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
  3. CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
  4. PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
  5. 值为0,表示当前节点在sync队列中,等待着获取锁。
Node prev 前驱节点,比如当前节点被取消,那就需要前驱节点和后继节点来完成连接。
Node next 后继节点。
Node nextWaiter 存储condition队列中的后继节点。
Thread thread 入队列时的当前线程。

节点成为sync队列和condition队列构建的基础,在同步器中就包含了sync队列。同步器拥有三个成员变量:sync队列的头结点head、sync队列的尾节点tail和状态state。对于锁的获取,请求形成节点,将其挂载在尾部,而锁资源的转移(释放再获取)是从头部开始向后进行。对于同步器维护的状态state,多个线程对其的获取将会产生一个链式的结构。

API说明

实现自定义同步器时,需要使用同步器提供的getState()、setState()和compareAndSetState()方法来操纵状态的变迁。

方法名称 描述
protected boolean tryAcquire(int arg) 排它的获取这个状态。这个方法的实现需要查询当前状态是否允许获取,然后再进行获取(使用compareAndSetState来做)状态。
protected boolean tryRelease(int arg)  释放状态。
protected int tryAcquireShared(int arg) 共享的模式下获取状态。
protected boolean tryReleaseShared(int arg) 共享的模式下释放状态。
protected boolean isHeldExclusively() 在排它模式下,状态是否被占用。

实现这些方法必须是非阻塞而且是线程安全的,推荐使用该同步器的父类java.util.concurrent.locks.AbstractOwnableSynchronizer来设置当前的线程。
开始提到同步器内部基于一个FIFO队列,对于一个独占锁的获取和释放有以下伪码可以表示。
获取一个排他锁。

01 while(获取锁)
{
02     if (获取到)
{
03         退出while循环
04     else {

抱歉!评论已关闭.