前言

AQS(AbstractQueuedSynchronizer)算是JUC包中最重要的一个类了,如果你想了解JUC提供的并发编程工具类的代码逻辑,这个类绝对是你绕不过的。我相信如果你是第一次看AQS源码肯定是一脸懵逼,一个个方法跳来跳去一会就绕蒙了。所以把整个代码骨架搞明白是你看懂AQS源码的第一步。本篇文章只说代码结构,之后的篇章会讲解AQS具体的执行逻辑。

顶级接口Lock

public interface Lock {

    void lock();

    void unlock();

    void lockInterruptibly() throws InterruptedException;

    boolean tryLock();

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    Condition newCondition();
}

我们一直强调面向接口编程的思想,看源码也是先从接口入手。Lock算是JUC提供的锁中的顶级接口,我们平常使用的ReentrantLockReadWriteLock等直接或间接的都实现了这个接口。这个接口主要是用来规定我们怎样去加锁和解锁,将加锁和解锁的方法暴露出去。下面的伪代码是一个典型的加锁解锁方式,多么简单,这就是面向接口的艺术。

 Lock l = ...;
l.lock();
try {
// 自己的逻辑代码
} finally {
l.unlock();
}

代码结构

到现在我们知道了怎么加锁和解锁的方式,下一步自己通过接口去实现一个加锁类。这个比你直接看源码更重要。Doug Lea(AQS源码作者)大神在AQS类注解上就给我们提供了一个示例类Mutex看源码类注解和方法注解也是你理解源码的一个重要渠道,还可以锻炼自己的英文。

实现Lock接口

首先要实现Lock接口,将加锁和解锁方法暴露出去。我们以加锁为例

class Mutex implements Lock, java.io.Serializable {

    public void lock() {
sync.acquire(1);
}

代码很简单,调用了sync.acquire(1)方法,所以这个方法是加锁逻辑的入口,往下看

内部类继承AQS

现在将加锁方法暴露出去了,具体的加锁逻辑则需要AQS类了。AQS是一个抽象类,我们要使用它则需要继承,实现抽象方法。AQS加锁采用的是模板的设计模式加锁以及锁失败的后续处理的整体流程代码已经实现,我们只需要实现我们需要的具体加锁方式即可。

class Mutex implements Lock, java.io.Serializable {

   // The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync(); // 内部类继承AQS
private static class Sync extends AbstractQueuedSynchronizer { // 实现抽象方法
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
}
}

我们会发现Sync继承了AQS,但是没有sync.acquire()这个方法,那么这个方法肯定来源于父类了。

AQS的acquire方法

public abstract class AbstractQueuedSynchronizer{

  //加锁的入口方法,模板的设计模式
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
} //具体的加锁逻辑,需要自己实现
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}

acquire方法定义了整个的加锁流程,而且使用了设计模式中的模板模式。

整体调用流程

从上面的流程可以看出,就这四个步骤就涉及到了三个类

  • Mutex.lock为暴露出去的加锁方法

  • AQS.acquire是加锁的模板方法,实现了加锁逻辑的整个流程

  • Sync.tryAcquire,图中的绿色部分,是一个抽象方法需要自己实现,针对不同的锁类型如公平锁、非公平锁、共享锁、独占锁等有不同的实现方式。

  • AQS.acquireQueued是加锁失败后的逻辑,将线程入队,这个后面讲AQS源码会重点讲。

    解锁操作的流程和加锁类似,读者可以自己看一下解锁的流程。

自定义加锁类的源码

下面的代码是Mutex类的整体代码,有需要的可以在自己的IDE中感受一下整体的结构。

class Mutex implements Lock, java.io.Serializable {

    public static void main(String[] args) {
Lock lock = new Mutex();
lock.lock();
try { }finally {
lock.unlock();
}
} public void lock() {
sync.acquire(1);
} public void unlock() {
sync.release(1);
} // Our internal helper class
private static class Sync extends AbstractQueuedSynchronizer {
// Reports whether in locked state
protected boolean isHeldExclusively() {
return getState() == 1;
} // Acquires the lock if state is zero
public boolean tryAcquire(int acquires) {
assert acquires == 1; // Otherwise unused
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
} // Releases the lock by setting state to zero
protected boolean tryRelease(int releases) {
assert releases == 1; // Otherwise unused
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
} // Provides a Condition
Condition newCondition() {
return new ConditionObject();
} // Deserializes properly
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
} // The sync object does all the hard work. We just forward to it.
private final Sync sync = new Sync(); public boolean tryLock() {
return sync.tryAcquire(1);
} public Condition newCondition() {
return sync.newCondition();
} public boolean isLocked() {
return sync.isHeldExclusively();
} public boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
} public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
} public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
}

下一篇我们将根据上面所说的来分析ReentrantLock类的代码结构

如有不实,还望指正

最新文章

  1. JS 阶段练习~ 仿flash的图片轮换效果
  2. setw()函数
  3. BAT及各大互联网公司前端笔试面试题--Html,Css篇
  4. bootstrap学习笔记<五>(表单一)
  5. ajax状态码
  6. char,wchar_t,WCHAR,TCHAR,ACHAR的区别----LPCTSTR
  7. Linux C SMTP POP3 极简陋邮件客户端
  8. C# - List操作 - 按照字母排序
  9. DirectX Sample-Blobs实现原理
  10. hdu 2149 Public Sale 简单博弈
  11. 这个时间格式2017-09-26-T04:00:00Z php识别不出来
  12. DriverStudio 和 WDF驱动 通过GUID获取设备句柄的差别
  13. 学习Qt,Getting started
  14. Spring Cloud 2-Feign 声明式服务调用(三)
  15. Client does not support authentication
  16. Redhat更换yum源
  17. hql 语法详解
  18. luogu3426 [POI2005]SZA-Template 后缀树
  19. 【做题】agc016d - XOR Replace——序列置换&环
  20. java代码------charAt()的用法

热门文章

  1. MySQL笔记(4)-- 索引优化
  2. AdFind
  3. React Native新项目启动报错'React/RCTBridgeDelegate.h' file not found
  4. Magento2(麦进斗) docker 安装
  5. element-ui 的 单选按钮(Radio) 怎么取消文本显示?
  6. UVA - 11426 欧拉函数(欧拉函数表)
  7. 【Pytest05】全网最全最新的Pytest框架之用例分组执行
  8. mabatis入门五 高级结果映射
  9. Python查看3Dnii文件
  10. Git 命令实战入门 ,奶妈级教程