了解了ReentrantLock和Semaphore实现原理,不难看出AQS担当着核心左右,AQS为独占锁和共享锁提供了基础框架能力,通过对AQS的封装,我们可以轻松实现线程状态管理。
AQS对象组成
对于AQS而言,其核心为state和先进先出的等待队列,其中state使用volatile修饰,保证其可见性和有序性,先进先出的等待队列使用双向链表实现,对于一个AQS对象而言,其组成如下图所示:
AQS同步等待队列
AQS中同步等待队列通过Node对象链表实现,一个完整的等待队列如下图所示:
上述中enq函数代码如下:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
// 初始化head和tail,刚开始head,tail都指向这里的new Node
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
完整的AQS数据结构(同步队列+条件队列)
实现AQS(根据业务逻辑自定义锁)
为了便于开发者给予AQS实现一个自定义锁,AQS主要提供了5个函数供大家覆写,对于没被子类覆写的函数,在其调用时会抛出UnsupportedOperationException异常,如下所示:
函数名称 | 函数说明 | 默认实现 | 适用锁类型 | 备注 |
tryAcquire | 尝试获取锁,主要进行锁状态标志state的操作,操作成功后更新state和当前持锁线程 | 抛出UnsupportedOperationException异常 | 独占锁 | / |
tryRelease | 尝试释放锁,更新锁状态标志state并设置当前持锁线程为null | 抛出UnsupportedOperationException异常 | 独占锁 | / |
tryAcquireShared | 共享模式下尝试获取锁 | 抛出UnsupportedOperationException异常 | 共享锁 | / |
tryReleaseShared | 共享模式下尝试释放锁 | 抛出UnsupportedOperationException异常 | 共享锁 | / |
isHeldExclusively | 判断锁是否被占用 | 抛出UnsupportedOperationException异常 | 独占锁/共享锁 | / |
下面我们来实现一个不可重入的独占锁(非公平),代码如下:
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class Mutex implements Lock, Serializable {
private static class Sync extends AbstractQueuedSynchronizer{
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0,1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newCondition() { return new ConditionObject(); }
}
private 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();
}
}
编写测试代码如下:
public static void main(String[] args) {
Mutex mutex = new Mutex();
ExecutorService testSynchronizedBlock = Executors.newCachedThreadPool();
testSynchronizedBlock.execute(new Runnable() {
@Override
public void run() {
// 首次获取锁运行2s后再次获取锁,模拟线程重入操作
System.out.println(Thread.currentThread().getName()+" enter lock first");
mutex.lock();
System.out.println(Thread.currentThread().getName()+" enter lock");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+" enter lock again");
mutex.lock();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+" exit lock again");
mutex.unlock();
System.out.println(Thread.currentThread().getName()+" exit lock");
mutex.unlock();
}
});
testSynchronizedBlock.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" enter lock before");
mutex.lock();
System.out.println(Thread.currentThread().getName()+" enter lock");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+" exit lock");
mutex.unlock();
}
});
}
执行结果如下:
可以看到线程pool-1-thread-1和pool-1-thread-2均卡在mutex 锁处,且形成死锁,长期无返回,修改Mutex为ReentrantLock,可以看到代码正常运行,结果如下:
这也进一步验证了ReentrantLock可重入的特性。
AQS的常见实现
从前文可知,ReentrantLock和Semaphore都是基于AQS实现,那系统中还有哪些类依赖AQS实现呢?见下表:
类名 | 独占锁/共享锁 | 公平锁/非公平锁 | 可重入 | 备注 |
ReentrantLock | 独占锁 | 都支持 | 是 | / |
ReentrantReadWriteLock | 读锁是共享锁/写锁是独占锁(读写锁) | 都支持 | 是 | / |
Semaphore | 共享锁 | 都支持 | 否 | / |
CountDownLatch | 共享锁 | 公平锁 | 否 | / |
AQS,synchronized与volatile
AQS取其代表实现ReentrantLock,三者比较如下表所示:
名称 | 底层实现 | 独占锁/共享锁 | 公平锁/非公平锁 | 可重入 | 重量级锁/轻量级锁 | 多线程特性 | 是否是锁 |
ReentrantLock | java层锁 | 都支持 | 都支持 | 是 | 重量级锁 | 原子性,有序性,可见性 | 是 |
synchronized | monitorenter/monitoreexit指令(Lock) | 独占锁 | 非公平锁 | 是 | 锁升级,先轻量级锁,再重量级锁 | 原子性,可见性,有序性 | 是 |
volatile | 内存屏障指令 | / | / | / | / | 可见性,有序性 | 否 |
原文始发于微信公众号(小海编码日记):AQS实现原理
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/67725.html