Java工程师知识树 / Java基础
AbstractOwnableSynchronizer类与其子类
AbstractOwnableSynchronizer
java.util.concurrent.locks.AbstractOwnableSynchronizer
:可以由线程以独占方式拥有的同步器。
AbstractOwnableSynchronizer
源码
package java.util.concurrent.locks;
/**
* 可由线程独占的同步器。
* 该类为创建锁和相关的同步器提供了基础,可能需要拥有所有权的概念。
* AbstractOwnableSynchronizer 类本身不管理或使用此信息。
* 但是,子类和工具可以使用适当维护的值来帮助控制和监视访问并提供诊断。
*
* @since 1.6
* @author Doug Lea
*/
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable {
/** 即使所有的字段都是短暂的(被禁止序列化的),也要使用 序列ID。*/
private static final long serialVersionUID = 3737899427754241961L;
/**
* 供子类使用的空的构造方法。
*/
protected AbstractOwnableSynchronizer() { }
/**
* 独占模式同步下的当前(线程)拥有者。(也就是当前独占锁的线程)
*/
private transient Thread exclusiveOwnerThread;
/**
* 设置 当前拥有独占访问的 线程。
* 空参数指示没有线程拥有访问权限。
* 此方法不另外施加任何同步化易失性 volatile 字段访问。
* @param thread 所有者的线程(拥有当前独占访问权的线程)
*/
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
/**
* 返回 setExclusiveOwnableThread(Thread thread) 方法最后设置的线程,
* 如果从未设置,则返回 null。
* 此方法不另外施加任何同步化易失性 volatile 字段访问。
* @return 所有者的线程(拥有当前独占访问权的线程)
*/
protected final Thread getExclusiveOwnerThread() { return exclusiveOwnerThread;
}
}
AbstractQueuedSynchronizer
AbstractQueuedSynchronizer是AbstractOwnableSynchronizer的子类。
该类被设计为大多数同步组件的基类,这些同步组件都依赖于单个原子值(int)来控制同步状态,子类必须要定义获取获取同步与释放状态的方法,在AQS中提供了三种方法getState()
、setState(int newState)
及compareAndSetState(int expect, int update)
来进行操作。
同时子类应该为自定义同步组件的静态内部类,AQS自身没有实现任何同步接口,它仅仅是定义了若干同步状态获取和释放的方法来供自定义同步组件使用,同步器既可以支持独占式地获取同步状态,也可以支持共享式地获取同步状态,这样就可以方便实现不同类型的同步组件(ReentrantLock、ReentrantReadWriteLock和CountDownLatch等)。
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable
AbstractQueuedLongSynchronizer
AbstractQueuedLongSynchronizer
以 long 形式维护同步状态的一个 AbstractQueuedSynchronizer
版本。
AbstractQueuedLongSynchronizer
具有的结构、属性和方法与 AbstractQueuedSynchronizer
完全相同,但所有与状态相关的参数和结果都定义为 long 而不是 int。
AQS简介
AQS是 AbstractQueuedSynchronizer的简称,即 抽象队列同步器 :
- 抽象:抽象类,只实现⼀些主要逻辑,有些⽅法由⼦类实现;
- 队列:使⽤先进先出(FIFO)队列存储数据;
- 同步:实现了同步的功能。
那AQS有什么⽤呢?
AQS是⼀个⽤来构建锁和同步器的框架,使⽤AQS能简单且⾼效地构造出应⽤⼴泛的同步器,⽐如我们提到的ReentrantLock
,Semaphore
,ReentrantReadWriteLock
,SynchronousQueue
,FutureTask
等等皆是基于AQS的。
AQS的数据结构
AQS内部使⽤了⼀个volatile的变量state来作为资源的标识。同时定义了⼏个获取和修改state的protected⽅法,⼦类可以覆盖这些⽅法来实现⾃⼰的逻辑:
int getState() :获取当前同步状态
void setState(int newState) :设置当前同步状态
boolean compareAndSetState(int expect, int update) 使用CAS设置当前状态。
这三种操作均是原⼦操作,其中compareAndSetState
的实现依赖于Unsafe的compareAndSwapInt()
⽅法。
⽽AQS类本身实现的是一个排队和阻塞的机制,⽐如具体线程等待队列的维护(如获取资源失败⼊队/唤醒出队等)。它内部使⽤了⼀个先进先出(FIFO)的双端队列,并使⽤了两个指针head和tail⽤于标识队列的头部和尾部。
AQS数据结构如图: 队列并不是直接储存线程,⽽是储存拥有线程的节点。
核心数据结构:双向链表 + state(锁状态)
底层操作:CAS
AbstractQueuedSynchronizer拥有的子类中可以重写的方法
boolean isHeldExclusively():当前线程是否独占锁
boolean tryAcquire(int arg):独占式尝试获取同步状态,通过CAS操作设置同步状态,如果成功返回true,反之返回false
boolean tryRelease(int arg):独占式释放同步状态。
int tryAcquireShared(int arg):共享式的获取同步状态,返回大于等于0的值,表示获取成功,反之失败。
boolean tryReleaseShared(int arg):共享式释放同步状态。
资源共享模式
资源共享模式介绍
资源有两种共享模式,或者说两种同步⽅式:
独占模式(Exclusive):资源是独占的,⼀次只能⼀个线程获取。如ReentrantLock。
共享模式(Share):同时可以被多个线程获取,具体的资源个数可以通过参 数指定。如Semaphore/CountDownLatch。
⼀般情况下,⼦类只需要根据需求实现其中⼀种模式,当然也有同时实现两种模式的同步类,如ReadWriteLock 。
java.util.concurrent.locks.AbstractQueuedSynchronizer.Node
源码
static final class Node {
// 标记⼀个结点(对应的线程)在共享模式下等待
static final Node SHARED = new Node();
// 标记⼀个结点(对应的线程)在独占模式下等待
static final Node EXCLUSIVE = null;
// waitStatus的值,表示该结点(对应的线程)已被取消
static final int CANCELLED = 1;
// waitStatus的值,表示后继结点(对应的线程)需要被唤醒
static final int SIGNAL = -1;
// waitStatus的值,表示该结点(对应的线程)在等待某⼀条件
static final int CONDITION = -2;
//waitStatus的值,表示有资源可⽤,新head结点需要继续唤醒后继结点(共享模式下,多线程并
static final int PROPAGATE = -3;
// 等待状态,取值范围,-3,-2,-1,0,1
volatile int waitStatus;
volatile Node prev; // 前驱结点
volatile Node next; // 后继结点
volatile Thread thread; // 结点对应的线程
Node nextWaiter; // 等待队列⾥下⼀个等待条件的结点
// 判断共享模式的⽅法
final boolean isShared() {
return nextWaiter == SHARED;
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
}
// AQS(AbstractQueuedSynchronizer)⾥⾯的addWaiter私有⽅法
private Node addWaiter(Node mode) {
// 使⽤了Node的这个构造函数
Node node = new Node(Thread.currentThread(), mode);
...
}
注意:通过Node我们可以实现两个队列,⼀是通过prev和next实现CLH队列(线程同步队列,双向队列),⼆是nextWaiter实现Condition条件上的等待线程队列(单向队列),这个Condition主要⽤在ReentrantLock类中。
自定义同步器
不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。
自定义同步器实现时主要实现以下几种方法:
- isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
- tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
- tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
- tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
- tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
以ReentrantLock为例,state初始化为0,表示未锁定状态。A线程lock()时,会调用tryAcquire()独占该锁并将state+1。此后,其他线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁。当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。
再以CountDownLatch以例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。
一般来说,自定义同步器要么是独占方法,要么是共享方式,他们也只需实现tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一种即可。
基于AQS实现不可重入的独占锁
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* 基于AQS实现不可重入的独占锁:
* 可重入锁, 每次进入一次state就加1 出去一次就减1
* 不可重入锁,定义state为1表示锁已经被某个线程获取了, 0表示锁还在 不需要记录获取锁的次数
* 这个是个不可重入锁,且支持条件变量
*/
public class NonReentrantLock implements Lock {
private final Sync sync = new Sync();
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(time));
}
@Override
public void unlock() {
sync.release(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
/**
* 辅助内部类
* 继承AQS 重写一些方法实现自己想要的功能
* 1.获取锁状态 通过state
* 2.尝试获取锁 设置state为1
* 3.尝试释放锁 设置state为0
* 4.提供条件变量接口
*/
private static class Sync extends AbstractQueuedSynchronizer {
/**
* 是否锁已经被持有
*/
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
/**
* 尝试获取锁
*/
@Override
public boolean tryAcquire(int acquires) {
//如果state为0 设置为1 成功
if (compareAndSetState(0, 1)) {
//设置独占模式的拥有者
setExclusiveOwnerThread(Thread.currentThread());
return true;
} else {
return false;
}
}
@Override
protected boolean tryRelease(int releases) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
//清空独占模式的拥有者
setExclusiveOwnerThread(null);
setState(0);
return true;
}
/**
* 提供条件变量接口
*/
Condition newCondition() {
return new ConditionObject();
}
}
/**
* 使用这个锁实现生产者消费者模型
*/
public static void main(String[] args) {
NonReentrantLock reentrantLock = new NonReentrantLock();
Condition full = reentrantLock.newCondition();
Condition empty = reentrantLock.newCondition();
int size = 5;
Queue queue = new LinkedBlockingQueue(size);
Thread producer = new Thread(() -> {
while (true) {
reentrantLock.lock();
//满了
if (queue.size() == size) {
//阻塞
try {
full.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
queue.add("a");
empty.signal();
}
reentrantLock.unlock();
}
});
Thread comsumer = new Thread(() -> {
while (true) {
reentrantLock.lock();
//不空
if (queue.size() != 0) {
String a = (String) queue.poll();
System.out.println("消费者消费" + a);
full.signal();
} else {
try {
empty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
reentrantLock.unlock();
}
});
producer.start();
comsumer.start();
}
}