mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-06-20 22:17:09 +08:00
Update AQS.md
This commit is contained in:
parent
25062ef6cf
commit
a88a412075
@ -1,30 +1,33 @@
|
|||||||
|
点击关注[公众号](#公众号 "公众号")及时获取笔主最新更新文章,并可免费领取本文档配套的《Java 面试突击》以及 Java 工程师必备学习资源。
|
||||||
|
|
||||||
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
|
<!-- TOC -->
|
||||||
<!-- MarkdownTOC -->
|
|
||||||
|
|
||||||
- [1 AQS 简单介绍](#1-aqs-简单介绍)
|
- [1 AQS 简单介绍](#1-aqs-简单介绍)
|
||||||
- [2 AQS 原理](#2-aqs-原理)
|
- [2 AQS 原理](#2-aqs-原理)
|
||||||
- [2.1 AQS 原理概览](#21-aqs-原理概览)
|
- [2.1 AQS 原理概览](#21-aqs-原理概览)
|
||||||
- [2.2 AQS 对资源的共享方式](#22-aqs-对资源的共享方式)
|
- [2.2 AQS 对资源的共享方式](#22-aqs-对资源的共享方式)
|
||||||
- [2.3 AQS底层使用了模板方法模式](#23-aqs底层使用了模板方法模式)
|
- [2.3 AQS 底层使用了模板方法模式](#23-aqs-底层使用了模板方法模式)
|
||||||
- [3 Semaphore\(信号量\)-允许多个线程同时访问](#3-semaphore信号量-允许多个线程同时访问)
|
- [3 Semaphore(信号量)-允许多个线程同时访问](#3-semaphore信号量-允许多个线程同时访问)
|
||||||
- [4 CountDownLatch (倒计时器)](#4-countdownlatch-倒计时器)
|
- [4 CountDownLatch (倒计时器)](#4-countdownlatch-倒计时器)
|
||||||
- [4.1 CountDownLatch 的三种典型用法](#41-countdownlatch-的三种典型用法)
|
- [4.1 CountDownLatch 的三种典型用法](#41-countdownlatch-的三种典型用法)
|
||||||
- [4.2 CountDownLatch 的使用示例](#42-countdownlatch-的使用示例)
|
- [4.2 CountDownLatch 的使用示例](#42-countdownlatch-的使用示例)
|
||||||
- [4.3 CountDownLatch 的不足](#43-countdownlatch-的不足)
|
- [4.3 CountDownLatch 的不足](#43-countdownlatch-的不足)
|
||||||
- [4.4 CountDownLatch相常见面试题:](#44-countdownlatch相常见面试题)
|
- [4.4 CountDownLatch 相常见面试题:](#44-countdownlatch-相常见面试题)
|
||||||
- [5 CyclicBarrier\(循环栅栏\)](#5-cyclicbarrier循环栅栏)
|
- [5 CyclicBarrier(循环栅栏)](#5-cyclicbarrier循环栅栏)
|
||||||
- [5.1 CyclicBarrier 的应用场景](#51-cyclicbarrier-的应用场景)
|
- [5.1 CyclicBarrier 的应用场景](#51-cyclicbarrier-的应用场景)
|
||||||
- [5.2 CyclicBarrier 的使用示例](#52-cyclicbarrier-的使用示例)
|
- [5.2 CyclicBarrier 的使用示例](#52-cyclicbarrier-的使用示例)
|
||||||
- [5.3 CyclicBarrier和CountDownLatch的区别](#53-cyclicbarrier和countdownlatch的区别)
|
- [5.3 `CyclicBarrier`源码分析](#53-cyclicbarrier源码分析)
|
||||||
|
- [5.4 CyclicBarrier 和 CountDownLatch 的区别](#54-cyclicbarrier-和-countdownlatch-的区别)
|
||||||
- [6 ReentrantLock 和 ReentrantReadWriteLock](#6-reentrantlock-和-reentrantreadwritelock)
|
- [6 ReentrantLock 和 ReentrantReadWriteLock](#6-reentrantlock-和-reentrantreadwritelock)
|
||||||
|
- [参考](#参考)
|
||||||
|
- [公众号](#公众号)
|
||||||
|
|
||||||
<!-- /MarkdownTOC -->
|
<!-- /TOC -->
|
||||||
|
|
||||||
> 常见问题:AQS 原理?;CountDownLatch 和 CyclicBarrier 了解吗,两者的区别是什么?用过 Semaphore 吗?
|
> 常见问题:AQS 原理?;CountDownLatch 和 CyclicBarrier 了解吗,两者的区别是什么?用过 Semaphore 吗?
|
||||||
|
|
||||||
|
|
||||||
### 1 AQS 简单介绍
|
### 1 AQS 简单介绍
|
||||||
|
|
||||||
AQS 的全称为(AbstractQueuedSynchronizer),这个类在 java.util.concurrent.locks 包下面。
|
AQS 的全称为(AbstractQueuedSynchronizer),这个类在 java.util.concurrent.locks 包下面。
|
||||||
|
|
||||||

|

|
||||||
@ -45,7 +48,6 @@ AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效
|
|||||||
|
|
||||||
看个 AQS(AbstractQueuedSynchronizer)原理图:
|
看个 AQS(AbstractQueuedSynchronizer)原理图:
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
AQS 使用一个 int 成员变量来表示同步状态,通过内置的 FIFO 队列来完成获取资源线程的排队工作。AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。
|
AQS 使用一个 int 成员变量来表示同步状态,通过内置的 FIFO 队列来完成获取资源线程的排队工作。AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。
|
||||||
@ -234,8 +236,6 @@ tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true
|
|||||||
- http://www.cnblogs.com/waterystone/p/4920797.html
|
- http://www.cnblogs.com/waterystone/p/4920797.html
|
||||||
- https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html
|
- https://www.cnblogs.com/chengxiao/archive/2017/07/24/7141160.html
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 3 Semaphore(信号量)-允许多个线程同时访问
|
### 3 Semaphore(信号量)-允许多个线程同时访问
|
||||||
|
|
||||||
**synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。** 示例代码如下:
|
**synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。** 示例代码如下:
|
||||||
@ -295,7 +295,6 @@ public class SemaphoreExample1 {
|
|||||||
|
|
||||||
除了 `acquire`方法之外,另一个比较常用的与之对应的方法是`tryAcquire`方法,该方法如果获取不到许可就立即返回 false。
|
除了 `acquire`方法之外,另一个比较常用的与之对应的方法是`tryAcquire`方法,该方法如果获取不到许可就立即返回 false。
|
||||||
|
|
||||||
|
|
||||||
Semaphore 有两种模式,公平模式和非公平模式。
|
Semaphore 有两种模式,公平模式和非公平模式。
|
||||||
|
|
||||||
- **公平模式:** 调用 acquire 的顺序就是获取许可证的顺序,遵循 FIFO;
|
- **公平模式:** 调用 acquire 的顺序就是获取许可证的顺序,遵循 FIFO;
|
||||||
@ -312,6 +311,7 @@ Semaphore 有两种模式,公平模式和非公平模式。
|
|||||||
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
|
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**这两个构造方法,都必须提供许可的数量,第二个构造方法可以指定是公平模式还是非公平模式,默认非公平模式。**
|
**这两个构造方法,都必须提供许可的数量,第二个构造方法可以指定是公平模式还是非公平模式,默认非公平模式。**
|
||||||
|
|
||||||
由于篇幅问题,如果对 Semaphore 源码感兴趣的朋友可以看下面这篇文章:
|
由于篇幅问题,如果对 Semaphore 源码感兴趣的朋友可以看下面这篇文章:
|
||||||
@ -374,6 +374,7 @@ public class CountDownLatchExample1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
上面的代码中,我们定义了请求的数量为 550,当这 550 个请求被处理完成之后,才会执行`System.out.println("finish");`。
|
上面的代码中,我们定义了请求的数量为 550,当这 550 个请求被处理完成之后,才会执行`System.out.println("finish");`。
|
||||||
|
|
||||||
与 CountDownLatch 的第一次交互是主线程等待其他线程。主线程必须在启动其他线程后立即调用 CountDownLatch.await()方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。
|
与 CountDownLatch 的第一次交互是主线程等待其他线程。主线程必须在启动其他线程后立即调用 CountDownLatch.await()方法。这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。
|
||||||
@ -400,6 +401,23 @@ CyclicBarrier 和 CountDownLatch 非常类似,它也可以实现线程间的
|
|||||||
|
|
||||||
CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier 默认的构造方法是 `CyclicBarrier(int parties)`,其参数表示屏障拦截的线程数量,每个线程调用`await`方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
|
CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier 默认的构造方法是 `CyclicBarrier(int parties)`,其参数表示屏障拦截的线程数量,每个线程调用`await`方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
|
||||||
|
|
||||||
|
再来看一下它的构造函数:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public CyclicBarrier(int parties) {
|
||||||
|
this(parties, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CyclicBarrier(int parties, Runnable barrierAction) {
|
||||||
|
if (parties <= 0) throw new IllegalArgumentException();
|
||||||
|
this.parties = parties;
|
||||||
|
this.count = parties;
|
||||||
|
this.barrierCommand = barrierAction;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
其中,parties 就代表了有拦截的线程的数量,当拦截的线程数量达到这个值的时候就打开栅栏,让所有线程通过。
|
||||||
|
|
||||||
#### 5.1 CyclicBarrier 的应用场景
|
#### 5.1 CyclicBarrier 的应用场景
|
||||||
|
|
||||||
CyclicBarrier 可以用于多线程计算数据,最后合并计算结果的应用场景。比如我们用一个 Excel 保存了用户所有银行流水,每个 Sheet 保存一个帐户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个 sheet 里的银行流水,都执行完之后,得到每个 sheet 的日均银行流水,最后,再用 barrierAction 用这些线程的计算结果,计算出整个 Excel 的日均银行流水。
|
CyclicBarrier 可以用于多线程计算数据,最后合并计算结果的应用场景。比如我们用一个 Excel 保存了用户所有银行流水,每个 Sheet 保存一个帐户近一年的每笔银行流水,现在需要统计用户的日均银行流水,先用多线程处理每个 sheet 里的银行流水,都执行完之后,得到每个 sheet 的日均银行流水,最后,再用 barrierAction 用这些线程的计算结果,计算出整个 Excel 的日均银行流水。
|
||||||
@ -482,6 +500,7 @@ threadnum:7is finish
|
|||||||
threadnum:6is finish
|
threadnum:6is finish
|
||||||
......
|
......
|
||||||
```
|
```
|
||||||
|
|
||||||
可以看到当线程数量也就是请求数量达到我们定义的 5 个的时候, `await`方法之后的方法才被执行。
|
可以看到当线程数量也就是请求数量达到我们定义的 5 个的时候, `await`方法之后的方法才被执行。
|
||||||
|
|
||||||
另外,CyclicBarrier 还提供一个更高级的构造函数`CyclicBarrier(int parties, Runnable barrierAction)`,用于在线程到达屏障时,优先执行`barrierAction`,方便处理更复杂的业务场景。示例代码如下:
|
另外,CyclicBarrier 还提供一个更高级的构造函数`CyclicBarrier(int parties, Runnable barrierAction)`,用于在线程到达屏障时,优先执行`barrierAction`,方便处理更复杂的业务场景。示例代码如下:
|
||||||
@ -559,7 +578,107 @@ threadnum:8is finish
|
|||||||
threadnum:7is finish
|
threadnum:7is finish
|
||||||
......
|
......
|
||||||
```
|
```
|
||||||
#### 5.3 CyclicBarrier和CountDownLatch的区别
|
|
||||||
|
#### 5.3 `CyclicBarrier`源码分析
|
||||||
|
|
||||||
|
当调用 `CyclicBarrier` 对象调用 `await()` 方法时,实际上调用的是`dowait(false, 0L)`方法。 `await()` 方法就像树立起一个栅栏的行为一样,将线程挡住了,当拦住的线程数量达到 parties 的值时,栅栏才会打开,线程才得以通过执行。
|
||||||
|
|
||||||
|
```java
|
||||||
|
public int await() throws InterruptedException, BrokenBarrierException {
|
||||||
|
try {
|
||||||
|
return dowait(false, 0L);
|
||||||
|
} catch (TimeoutException toe) {
|
||||||
|
throw new Error(toe); // cannot happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`dowait(false, 0L)`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 当线程数量或者请求数量达到 count 时 await 之后的方法才会被执行。上面的示例中 count 的值就为 5。
|
||||||
|
private int count;
|
||||||
|
/**
|
||||||
|
* Main barrier code, covering the various policies.
|
||||||
|
*/
|
||||||
|
private int dowait(boolean timed, long nanos)
|
||||||
|
throws InterruptedException, BrokenBarrierException,
|
||||||
|
TimeoutException {
|
||||||
|
final ReentrantLock lock = this.lock;
|
||||||
|
// 锁住
|
||||||
|
lock.lock();
|
||||||
|
try {
|
||||||
|
final Generation g = generation;
|
||||||
|
|
||||||
|
if (g.broken)
|
||||||
|
throw new BrokenBarrierException();
|
||||||
|
|
||||||
|
// 如果线程中断了,抛出异常
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
breakBarrier();
|
||||||
|
throw new InterruptedException();
|
||||||
|
}
|
||||||
|
// cout减1
|
||||||
|
int index = --count;
|
||||||
|
// 当 count 数量减为 0 之后说明最后一个线程已经到达栅栏了,也就是达到了可以执行await 方法之后的条件
|
||||||
|
if (index == 0) { // tripped
|
||||||
|
boolean ranAction = false;
|
||||||
|
try {
|
||||||
|
final Runnable command = barrierCommand;
|
||||||
|
if (command != null)
|
||||||
|
command.run();
|
||||||
|
ranAction = true;
|
||||||
|
// 将 count 重置为 parties 属性的初始化值
|
||||||
|
// 唤醒之前等待的线程
|
||||||
|
// 下一波执行开始
|
||||||
|
nextGeneration();
|
||||||
|
return 0;
|
||||||
|
} finally {
|
||||||
|
if (!ranAction)
|
||||||
|
breakBarrier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// loop until tripped, broken, interrupted, or timed out
|
||||||
|
for (;;) {
|
||||||
|
try {
|
||||||
|
if (!timed)
|
||||||
|
trip.await();
|
||||||
|
else if (nanos > 0L)
|
||||||
|
nanos = trip.awaitNanos(nanos);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
if (g == generation && ! g.broken) {
|
||||||
|
breakBarrier();
|
||||||
|
throw ie;
|
||||||
|
} else {
|
||||||
|
// We're about to finish waiting even if we had not
|
||||||
|
// been interrupted, so this interrupt is deemed to
|
||||||
|
// "belong" to subsequent execution.
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g.broken)
|
||||||
|
throw new BrokenBarrierException();
|
||||||
|
|
||||||
|
if (g != generation)
|
||||||
|
return index;
|
||||||
|
|
||||||
|
if (timed && nanos <= 0L) {
|
||||||
|
breakBarrier();
|
||||||
|
throw new TimeoutException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
总结:`CyclicBarrier` 内部通过一个 count 变量作为计数器,cout 的初始值为 parties 属性的初始化值,每当一个线程到了栅栏这里了,那么就将计数器减一。如果 count 值为 0 了,表示这是这一代最后一个线程到达栅栏,就尝试执行我们构造方法中输入的任务。
|
||||||
|
|
||||||
|
#### 5.4 CyclicBarrier 和 CountDownLatch 的区别
|
||||||
|
|
||||||
CountDownLatch 是计数器,只能使用一次,而 CyclicBarrier 的计数器提供 reset 功能,可以多次使用。但是我不那么认为它们之间的区别仅仅就是这么简单的一点。我们来从 jdk 作者设计的目的来看,javadoc 是这么描述它们的:
|
CountDownLatch 是计数器,只能使用一次,而 CyclicBarrier 的计数器提供 reset 功能,可以多次使用。但是我不那么认为它们之间的区别仅仅就是这么简单的一点。我们来从 jdk 作者设计的目的来看,javadoc 是这么描述它们的:
|
||||||
|
|
||||||
@ -572,21 +691,23 @@ CountDownLatch是计数器,线程完成一个记录一个,只不过计数不
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
CyclicBarrier和CountDownLatch的区别这部分内容参考了如下两篇文章:
|
|
||||||
|
|
||||||
- https://blog.csdn.net/u010185262/article/details/54692886
|
|
||||||
- https://blog.csdn.net/tolcf/article/details/50925145?utm_source=blogxgwz0
|
|
||||||
|
|
||||||
### 6 ReentrantLock 和 ReentrantReadWriteLock
|
### 6 ReentrantLock 和 ReentrantReadWriteLock
|
||||||
|
|
||||||
ReentrantLock 和 synchronized 的区别在上面已经讲过了这里就不多做讲解。另外,需要注意的是:读写锁 ReentrantReadWriteLock 可以保证多个线程可以同时读,所以在读操作远大于写操作的时候,读写锁就非常有用了。
|
ReentrantLock 和 synchronized 的区别在上面已经讲过了这里就不多做讲解。另外,需要注意的是:读写锁 ReentrantReadWriteLock 可以保证多个线程可以同时读,所以在读操作远大于写操作的时候,读写锁就非常有用了。
|
||||||
|
|
||||||
## 公众号
|
### 参考
|
||||||
|
|
||||||
|
- https://juejin.im/post/5ae755256fb9a07ac3634067
|
||||||
|
- https://blog.csdn.net/u010185262/article/details/54692886
|
||||||
|
- https://blog.csdn.net/tolcf/article/details/50925145?utm_source=blogxgwz0
|
||||||
|
|
||||||
|
### 公众号
|
||||||
|
|
||||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||||
|
|
||||||
**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取!
|
**《Java 面试突击》:** 由本文档衍生的专为面试而生的《Java 面试突击》V2.0 PDF 版本[公众号](#公众号 "公众号")后台回复 **"面试突击"** 即可免费领取!
|
||||||
|
|
||||||
**Java 工程师必备学习资源:** 一些 Java 工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
|
**Java 工程师必备学习资源:** 一些 Java 工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user