juc其实就是包的缩写(java.util.concurrnt),通过下图展示了JUC里面有什么类。AbstractQueuedSynchronizer(抽象队列同步器,简称AQS),就是J.U.C包的骨架,基于AQS,J.U.C包得以实现了的重入锁、读写锁、CountDownLatch(计数锁)、Semaphore(信号量)和FutureTask等类。
AQS中主要维护了state(锁状态的表示)和一个可阻塞的等待队列。
state是临界资源,也是锁的描述。表示有多少线程获取了锁。
private volatile int state;
关于state的get,set方法就不贴了,重要的是有个通过CAS修改state的方法。
//设置期望值,想修改的值。通过CAS操作实现。
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
除此之外,还维护了等待队列(也叫CHL队列,同步队列)的头节点和尾节点。
private transient volatile Node head;
private transient volatile Node tail;
CHL队列由链表实现,以自旋的方式获取资源,是可阻塞的先进先出的双向队列。通过自旋和CAS操作保证节点插入和移除的原子性。当有线程获取锁失败,就被添加到队列末尾。下面我们来看看每个节点是怎么实现的。
二、内部类-Node
AQS的工作模式分为独占模式和共享模式,记录在节点的信息中。它还使用了模板方法设计模式,定义一个操作中算法的骨架,而将一些步骤的实现延迟到子类中。比如获取资源的方法就能很好的品味模板模式。一般地,它的实现类只实现一种模式,ReentrantLock就实现了独占模式;但也有例外,ReentrantReadAndWriteLock实现了独占模式和共享模式。下面来看Node相关源码。
//当前节点处于共享模式的标记
static final Node SHARED = new Node();
//当前节点处于独占模式的标记
static final Node EXCLUSIVE = null;
//线程被取消了
static final int CANCELLED = 1;
//释放资源后需唤醒后继节点
static final int SIGNAL = -1;
//等待condition唤醒
static final int CONDITION = -2;
//工作于共享锁状态,需要向后传播,
//比如根据资源是否剩余,唤醒后继节点
static final int PROPAGATE = -3;
//等待状态,有1,0,-1,-2,-3五个值。分别对应上面的值
volatile int waitStatus;
//前驱节点
volatile Node prev;
//后继节点
volatile Node next;
//等待锁的线程
volatile Thread thread;
//等待条件的下一个节点,ConditonObject中用到
Node nextWaiter;
对于等待状态(waitStatus)做一个解释。
- CANCELLED =1 线程被取消了
- SIGNAL =-1 释放资源后需唤醒后继节点
- CONDITION = -2 等待condition唤醒
- PROPAGATE = -3 (共享锁)状态需要向后传播
- 0 初始状态,正常状态
CANCELLED
作废状态,该节点的线程由于超时,中断等原因而处于作废状态。是不可逆的,一旦处于这个状态,说明应该将该节点移除了。
SIGNAL
待唤醒后继状态,当前节点的线程处于此状态,后继节点会被挂起,当前节点释放锁或取消之后必须唤醒它的后继节点。
CONDITION
等待状态,表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。