Java多线程源码分析之:AQS常用应用
Java多线程源码分析之:AQS常用应用
之前我们说到了aqs的基本方法,主要是acquire()获取锁和release()释放锁,这是aqs中阻塞队列的方法,还有两个条件队列的基本方法:await()等待和signal。当aqs里面还有不少扩展方法,它们其实是为了支持不同的并发工具来定义的。
常见的主要有:
- Semaphore:可以理解为操作系统中的信号量,可以看作一种共享锁。
- CountDownLatch:可以看作一种计数器,在没到达指定数字时会阻塞所有线程,否则就会同时唤醒所有线程。(计数器是递减,到达0会释放所有线程)
- ReentrantLock:经典的互斥锁,它同样是AQS中条件队列的实现类。
- CyclicBarrier:它是基于ReentrantLock实现的,和CountDownLatch有些类似,也可以看作一种计数器,唯一的区别是当计数器到达0时会重置到开始的数字。
- ReentrantReadWriteLock:读写锁,提供读锁共享写锁阻塞。
- ThreadPoolExecutor:大名鼎鼎的线程池(线程池会单独成篇)
锁的白月光:ReentrantLock
ReentrantLock我没记错的话是我接触的第二个Java锁,第一个是使用最简单的synchronized。
按照惯例,先来看看ReentrantLock类的总体结构:
我买了可以看到,在ReentrantLock中,有三个内部类,包含了lock和unlock等方法。
其中,FairSync为抽象类,负责对AQS进行了进一步的封装,并实现了一些通用方法。
首先来看看FairSync类(我略去了一些不重要的代码)
abstract static class Sync extends AbstractQueuedSynchronizer {
//解锁,由子类实现
abstract void lock();
/**
* 非公平锁的实现
* 这个方法其实就是AQS中的tryAcquire()方法的实现类
* Sync的子类会通过此方法来实现AQS中的tryAcquire()方法
* (AQS中的tryAcquire()方法没有具体实现,默认抛出异常)
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
/**
* 如果看过我之前的AQS文章看到这里应该会一眼懂,这里就是在
* 用cas修改state的值,也就是获取锁
* 这里是第一次加锁的情况
*/
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
/**
* 这里其实是可重入锁的实现,只有持锁线程才能多次加锁
*/
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
{
throw new Error("Maximum lock count exceeded");
}
setState(nextc);
return true;
}
return false;
}
/**
* AQS中释放锁的实现类
* 代码其实不难,就是在更改state的值
*/
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException();
}
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}