问题

(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. [考试反思]1112csp-s模拟测试111:二重

    还是AK场.考前信心赛? 而且T3的部分分还放反了所有80的都其实只有50. 总算在AK场真正AK了一次... 手感好,整场考试很顺利.要不是因为T3是原题可能就没这么好看了. 20minT1,50m ...

  2. JavaScript Array filter() 方法

    JavaScript Array filter() 方法 var ages = [32, 33, 16, 40]; function checkAdult(age) { return age > ...

  3. C++入门到理解之文件操作(文本文件的读写+二进制文件的读写)

    原文地址http://www.javayihao.top/detail/168 一:概述 1.程序在运行中产生的数据都是临时数据,程序一旦运行结束会被释放,可以通过文件相关的操作将数据持久保存. 2. ...

  4. go语言之数据类型和格式化输出

    1.数据类型 package main import ( "fmt" "reflect" ) func main() { //整形 var v1 int32 v ...

  5. 三行Jquery代码实现简单的选项卡

    今晚,我们来用实现一个简单的选项卡切换代码,主要代码只有两行. 效果: 思路:通过切换JQuery控制div的显隐和样式的改变 其中那个一个div显示,其余全隐藏 实现: <!DOCTYPE h ...

  6. css样式优先级计算规则

    css样式的优先级分为引入优先级和声明优先级. 引入优先级 引入样式一般分为外部样式,内部样式,内联样式. 外部样式:使用link引入的外部css文件. 内部样式:使用style标签书写的css样式. ...

  7. 什么是EAC模型

    在20世纪70年代末,一个心理学学生理查德•班德勒和一个语言学学生约翰•格林德提出了一个EAC模型,即眼睛解读线索.这个模型对不同的感官和思维方式之间进行一些有效的研究, 对于大部分的人来说,左边往往 ...

  8. 松软科技web课堂:SQLServer之SUM() 函数

    SUM() 函数 SUM 函数返回数值列的总数(总额). SQL SUM() 语法 SELECT SUM(column_name) FROM table_name SQL SUM() 实例 我们拥有下 ...

  9. [转]RPA认证 Developer UIPath Certificate,细说uipath认证学习,Online Quiz和Practical Exam项目详解

    本文转自:https://blog.csdn.net/u010369735/article/details/88621195 UIPath,RPA里算是比较简单易操作的一款软件了,因为公司业务的需要, ...

  10. Last 2 dimensions of the array must be square

    这个报错是因为我们在求解行列式的值的时候使用了: np.linalg.det(D) 但是D必须是方阵才可以进行运算,不是方阵则会报错,我们把之前的行列式更改为方阵就不会再报错了,当然这也是numpy自 ...