本文重点
前情提要
上文中详细介绍了 AQS 源码的执行流程和核心思想, 如下。
- CAS
- 自旋
- LockSupport.park() unpark()
- 双端队列
AQS 中 tryAcquire / tryRelease, tryAcquireShared / tryReleaseShared 都需要具体子类根据不同的策略来实现,而具体的排队逻辑、控制加锁及释放都是在 AQS 中实现的
AQS 的子类
1、ReentrantLock
- 独占锁
- 可重入
- 公平与非公平
- 通过 CAS 设置 state,失败则进入 AQS 排队逻辑
- 如果是当前线程重入,则 state + 1
用法如下:
ReentrantLock lock = new ReentrantLock(true);
lock.lock();
try {
//do something
} finally {
lock.unlock();
}
2、ReentrantReadWriteLock
- 提供 ReadLock / WriteLock
- 读读共享, 读写、写写互斥
- ReadLock.lock() 执行 acquireShared()
- ReadLock.lock() 执行 acquire()
- 适合读多写少的场景
一个缓存字典的示例:
class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try {
return m.get(key);
}
finally {
r.unlock();
}
}
public String[] allKeys() {
r.lock();
try {
return m.keySet().toArray();
}
finally {
r.unlock();
}
}
public Data put(String key, Data value) {
w.lock();
try {
return m.put(key, value);
}
finally {
w.unlock();
}
}
public void clear() {
w.lock();
try {
m.clear();
}
finally {
w.unlock();
}
}
}
3、CountDownLatch
- 共享锁
- 核心是计数,初始化 state = count
- countDown() 是释放锁, state -1
- await(), 是尝试获取锁,获取不到则进入队列等待
- 适用于主线程等待各个子线程执行完毕后再继续执行
实例:
package com.lyqiang.aqs;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @author lyqiang
*/
public class CountDownLatchTest {
public static void main(String[] args) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
Student student = new Student(i, countDownLatch);
new Thread(student).start();
}
countDownLatch.await();
System.out