问题

(1)AQS的定位?

(2)AQS的重要组成部分?

(3)AQS运用的设计模式?

(4)AQS的总体流程?

简介

AQS的全称是AbstractQueuedSynchronizer,它的定位是为Java中几乎所有的锁和同步器提供一个基础框架。

在之前的章节中,我们一起学习了ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源码,今天我们一起来对AQS做个总结。

状态变量state

AQS中定义了一个状态变量state,它有以下两种使用方法:

(1)互斥锁

当AQS只实现为互斥锁的时候,每次只要原子更新state的值从0变为1成功了就获取了锁,可重入是通过不断把state原子更新加1实现的。

(2)互斥锁 + 共享锁

当AQS需要同时实现为互斥锁+共享锁的时候,低16位存储互斥锁的状态,高16位存储共享锁的状态,主要用于实现读写锁。

互斥锁是一种独占锁,每次只允许一个线程独占,且当一个线程独占时,其它线程将无法再获取互斥锁及共享锁,但是它自己可以获取共享锁。

共享锁同时允许多个线程占有,只要有一个线程占有了共享锁,所有线程(包括自己)都将无法再获取互斥锁,但是可以获取共享锁。

AQS队列

AQS中维护了一个队列,获取锁失败(非tryLock())的线程都将进入这个队列中排队,等待锁释放后唤醒下一个排队的线程(互斥锁模式下)。

Condition队列

AQS中还有另一个非常重要的内部类ConditionObject,它实现了Condition接口,主要用于实现条件锁。

ConditionObject中也维护了一个队列,这个队列主要用于等待条件的成立,当条件成立时,其它线程将signal这个队列中的元素,将其移动到AQS的队列中,等待占有锁的线程释放锁后被唤醒。

Condition典型的运用场景是在BlockingQueue中的实现,当队列为空时,获取元素的线程阻塞在notEmpty条件上,一旦队列中添加了一个元素,将通知notEmpty条件,将其队列中的元素移动到AQS队列中等待被唤醒。

模板方法

AQS这个抽象类把模板方法设计模式运用地炉火纯青,它里面定义了一系列的模板方法,比如下面这些:

// 获取互斥锁
public final void acquire(int arg) {
// tryAcquire(arg)需要子类实现
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 获取互斥锁可中断
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquire(arg)需要子类实现
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
// 获取共享锁
public final void acquireShared(int arg) {
// tryAcquireShared(arg)需要子类实现
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// 获取共享锁可中断
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// tryAcquireShared(arg)需要子类实现
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// 释放互斥锁
public final boolean release(int arg) {
// tryRelease(arg)需要子类实现
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// 释放共享锁
public final boolean releaseShared(int arg) {
// tryReleaseShared(arg)需要子类实现
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}

获取锁、释放锁的这些方法基本上都穿插在ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源码解析中了,现在看他们是不是舒服多了,如果一开始就看这些源码,难免会很晕。

需要子类实现的方法

上面一起学习了AQS中几个重要的模板方法,下面我们再一起学习下几个需要子类实现的方法:

// 互斥模式下使用:尝试获取锁
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 互斥模式下使用:尝试释放锁
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 共享模式下使用:尝试获取锁
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
// 共享模式下使用:尝试释放锁
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
// 如果当前线程独占着锁,返回true
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}

这几个方法为什么不直接定义成抽象方法呢?

因为子类只要实现这几个方法中的一部分就可以实现一个同步器了,所以不需要定义成抽象方法。

总结

今天我们大概讲了下AQS中几个重要的组成部分,搞明白了这几个结构,AQS对你将没有任何秘密可言,当然面试的时候能把这几个点答清楚,面试官也会眼前一亮的。

(1)状态变量state;

(2)AQS队列;

(3)Condition队列;

(4)模板方法;

(5)需要子类实现的方法;

彩蛋

经过前面的学习,您能简要描述一下AQS获取互斥锁的大体流程吗?

这里彤哥就不作答了,相信学习完前面的内容,答这道题不是个问题了,答不上来的还需要把下面的推荐阅读好好多看几遍^^

推荐阅读

1、 死磕 java同步系列之开篇

2、 死磕 java魔法类之Unsafe解析

3、 死磕 java同步系列之JMM(Java Memory Model)

4、 死磕 java同步系列之volatile解析

5、 死磕 java同步系列之synchronized解析

6、 死磕 java同步系列之自己动手写一个锁Lock

7、 死磕 java同步系列之AQS起篇

8、 死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁

9、 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁

10、 死磕 java同步系列之ReentrantLock VS synchronized

11、 死磕 java同步系列之ReentrantReadWriteLock源码解析

12、 死磕 java同步系列之Semaphore源码解析

13、 死磕 java同步系列之CountDownLatch源码解析

欢迎关注我的公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一起畅游源码的海洋。

死磕 java同步系列之AQS终篇(面试)的更多相关文章

  1. 死磕 java同步系列之AQS起篇

    问题 (1)AQS是什么? (2)AQS的定位? (3)AQS的实现原理? (4)基于AQS实现自己的锁? 简介 AQS的全称是AbstractQueuedSynchronizer,它的定位是为Jav ...

  2. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

  3. 死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

  4. 死磕 java同步系列之zookeeper分布式锁

    问题 (1)zookeeper如何实现分布式锁? (2)zookeeper分布式锁有哪些优点? (3)zookeeper分布式锁有哪些缺点? 简介 zooKeeper是一个分布式的,开放源码的分布式应 ...

  5. 死磕 java同步系列之redis分布式锁进化史

    问题 (1)redis如何实现分布式锁? (2)redis分布式锁有哪些优点? (3)redis分布式锁有哪些缺点? (4)redis实现分布式锁有没有现成的轮子可以使用? 简介 Redis(全称:R ...

  6. 死磕 java同步系列之终结篇

    简介 同步系列到此就结束了,本篇文章对同步系列做一个总结. 脑图 下面是关于同步系列的一份脑图,列举了主要的知识点和问题点,看过本系列文章的同学可以根据脑图自行回顾所学的内容,也可以作为面试前的准备. ...

  7. 死磕 java同步系列之StampedLock源码解析

    问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...

  8. 死磕 java同步系列之Semaphore源码解析

    问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...

  9. 死磕 java同步系列之ReentrantReadWriteLock源码解析

    问题 (1)读写锁是什么? (2)读写锁具有哪些特性? (3)ReentrantReadWriteLock是怎么实现读写锁的? (4)如何使用ReentrantReadWriteLock实现高效安全的 ...

随机推荐

  1. Linux下动态切换EHCI控制器下端口的速率(即切换为12M)

    在sys目录下找到对应的控制器 例如:/sys/devices/platform/soc/ehci,直接操作该目录下的companion echo 1  > companion  将port1设 ...

  2. 使用 html2canvas 点击保存时把当前页面生成图片

     style:   #box{     background-image:url('./img/pone.png')     }   body:   <div id="box" ...

  3. Linux系统目录结构知识

    1.Linux目录结构: 逻辑上所有的目录只有一个顶点 /(根),所有目录的起点.根下面是一个类似倒挂的树的层次结构. 目录的结构和分区设备是没有关系的,也就是不同的目录可以跨越不同的磁盘设备或分区. ...

  4. emojy表情的小问题

    报错内容:在emojy表情入库或者更新库的时候,会报这个错:java.sql.SQLException:Incorrect string value: '\xF0\x9F\x99\x8F\..' fo ...

  5. Java描述设计模式(04):抽象工厂模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.抽象工厂模式 1.生活场景 汽车生产根据用户选择的汽车类型,指定不同的工厂进行生产,选择红旗轿车,就要使用中国工厂,选择奥迪轿车,就要使用 ...

  6. Redis在Window下的安装部署

    一.下载 由于redis官方不支持windows,所以需要在github上下载window的版本:下载地址.redis约定版次版本号(即第一个小数点后的数字)为偶数的版本是稳定版本(如2.8,3.0) ...

  7. IE浏览器远程代码执行高危漏洞(CVE-2019-1367)

    IE浏览器远程代码执行高危漏洞(CVE-2019-1367)加固遇到的问题 一.背景介绍 Internet Explorer,是微软公司推出的一款网页浏览器.用户量极大.9月23日微软紧急发布安全更新 ...

  8. JS基础语法---对象总结

        * 编程思想:     * 面向过程:凡事亲力亲为,所有的事情的过程都要清楚,注重的是过程     * 面向对象:提出需求,找到对象,对象解决这个问题,我们要结果,注重的是结果     *   ...

  9. 简单的LNMP安全加固

    安全加固 Nginx安全加固: Nginx配置文件 (nginx.conf):/usr/local/nginx/nginx.conf 禁用autoindex: autoindex:是Nginx的目录浏 ...

  10. NumPy实现数据的聚合,计算最大值,最小值

    1.数组值的求和 首先构造一个具有100个值的数组,然后我们利用两个不同的方法进行求和: >>> l=np.random.random() l的数据如下: >>> ...