带你熟悉3种AQS的线程并发工具的用法

熟悉,aqs,线程,并发,工具,用法 · 浏览次数 : 98

小编点评

**代码示例** ```java //cyclic barrier example private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5); public static void main(String[] args) throws Exception { executorService.execute(() -> { try { race(1); } catch (Exception e) { e.printStackTrace(); } }); executorService.execute(() -> { try { race(2); } catch (Exception e) { e.printStackTrace(); } }); executorService.execute(() -> { try { race(3); } catch (Exception e) { e.printStackTrace(); } }); executorService.shutdown(); private static void race(int threadNum) throws Exception { Thread.sleep(1000); log.info("{} is ready", threadNum); try { cyclicBarrier.await(2000, TimeUnit.MILLISECONDS); } catch (BrokenBarrierException | TimeoutException e) { log.warn("BarrierException", e); } log.info("{} continue", threadNum); } } ``` **代码注释** * **cyclicBarrier** 是一个可以处理多个线程等待屏障的同步对象。 * **executorService** 是一个可以执行多个线程的线程池。 * **race** 方法是用于执行多个线程等待屏障的回调方法。 * **brokenBarrierException** 是一个表示屏障被中断的异常。 * **timeoutException** 是表示等待屏障超时的异常。 **排版** ``` //cyclic barrier example private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5); public static void main(String[] args) throws Exception { executorService.execute(() -> { try { race(1); } catch (Exception e) { e.printStackTrace(); } }); executorService.execute(() -> { try { race(2); } catch (Exception e) { e.printStackTrace(); } }); executorService.execute(() -> { try { race(3); } catch (Exception e) { e.printStackTrace(); } }); executorService.shutdown(); private static void race(int threadNum) throws Exception { Thread.sleep(1000); log.info("{} is ready", threadNum); try { cyclicBarrier.await(2000, TimeUnit.MILLISECONDS); } catch (BrokenBarrierException | TimeoutException e) { log.warn("BarrierException", e); } log.info("{} continue", threadNum); } } ```

正文

摘要:AQS 的全称为(AbstractQueuedSynchronizer),AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器。

本文分享自华为云社区《【高并发】AQS中的CountDownLatch、Semaphore与CyclicBarrier核心用法》,作者: 冰 河。

AQS 的全称为(AbstractQueuedSynchronizer),AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器。本文主要讲述AQS中的CountDownLatch、Semaphore与CyclicBarrier核心用法。

CountDownLatch

概述

同步辅助类,通过它可以阻塞当前线程。也就是说,能够实现一个线程或者多个线程一直等待,直到其他线程执行的操作完成。使用一个给定的计数器进行初始化,该计数器的操作是原子操作,即同时只能有一个线程操作该计数器。

调用该类await()方法的线程会一直阻塞,直到其他线程调用该类的countDown()方法,使当前计数器的值变为0为止。每次调用该类的countDown()方法,当前计数器的值就会减1。当计数器的值减为0的时候,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种操作只能出现一次,因为该类中的计数器不能被重置。如果需要一个可以重置计数次数的版本,可以考虑使用CyclicBarrier类。

CountDownLatch支持给定时间的等待,超过一定的时间不再等待,使用时只需要在await()方法中传入需要等待的时间即可。此时,await()方法的方法签名如下:

public boolean await(long timeout, TimeUnit unit)

使用场景

在某些业务场景中,程序执行需要等待某个条件完成后才能继续执行后续的操作。典型的应用为并行计算:当某个处理的运算量很大时,可以将该运算任务拆分成多个子任务,等待所有的子任务都完成之后,父任务再拿到所有子任务的运算结果进行汇总。

代码示例

调用ExecutorService类的shutdown()方法,并不会第一时间内把所有线程全部都销毁掉,而是让当前已有的线程全部执行完,之后,再把线程池销毁掉。

示例代码如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class CountDownLatchExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 test(threadNum);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }finally {
 countDownLatch.countDown();
 }
 });
 }
 countDownLatch.await();
        log.info("finish");
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 Thread.sleep(100);
 log.info("{}", threadNum);
 Thread.sleep(100);
 }
}

支持给定时间等待的示例代码如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Slf4j
public class CountDownLatchExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 test(threadNum);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }finally {
 countDownLatch.countDown();
 }
 });
 }
 countDownLatch.await(10, TimeUnit.MICROSECONDS);
        log.info("finish");
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 Thread.sleep(100);
 log.info("{}", threadNum);
 }
}

Semaphore

概述

控制同一时间并发线程的数目。能够完成对于信号量的控制,可以控制某个资源可被同时访问的个数。

提供了两个核心方法——acquire()方法和release()方法。acquire()方法表示获取一个许可,如果没有则等待,release()方法则是在操作完成后释放对应的许可。Semaphore维护了当前访问的个数,通过提供同步机制来控制同时访问的个数。Semaphore可以实现有限大小的链表。

使用场景

Semaphore常用于仅能提供有限访问的资源,比如:数据库连接数。

代码示例

每次获取并释放一个许可,示例代码如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
public class SemaphoreExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final Semaphore semaphore = new Semaphore(3);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 semaphore.acquire(); //获取一个许可
 test(threadNum);
 semaphore.release(); //释放一个许可
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 });
 }
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 log.info("{}", threadNum);
 Thread.sleep(1000);
 }
}

每次获取并释放多个许可,示例代码如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
public class SemaphoreExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final Semaphore semaphore = new Semaphore(3);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 semaphore.acquire(3); //获取多个许可
 test(threadNum);
 semaphore.release(3); //释放多个许可
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 });
 }
        log.info("finish");
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 log.info("{}", threadNum);
 Thread.sleep(1000);
 }
}

假设有这样一个场景,并发太高了,即使使用Semaphore进行控制,处理起来也比较棘手。假设系统当前允许的最高并发数是3,超过3后就需要丢弃,使用Semaphore也能实现这样的场景,示例代码如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
@Slf4j
public class SemaphoreExample {
 private static final int threadCount = 200;
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newCachedThreadPool();
 final Semaphore semaphore = new Semaphore(3);
 for (int i = 0; i < threadCount; i++){
 final int threadNum = i;
 exec.execute(() -> {
 try {
 //尝试获取一个许可,也可以尝试获取多个许可,
 //支持尝试获取许可超时设置,超时后不再等待后续线程的执行
 //具体可以参见Semaphore的源码
 if (semaphore.tryAcquire()) { 
 test(threadNum);
 semaphore.release(); //释放一个许可
 }
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 });
 }
        log.info("finish");
 exec.shutdown();
 }
 private static void test(int threadNum) throws InterruptedException {
 log.info("{}", threadNum);
 Thread.sleep(1000);
 }
}

CyclicBarrier

概述

是一个同步辅助类,允许一组线程相互等待,直到到达某个公共的屏障点,通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。

与CountDownLatch有相似的地方,都是使用计数器实现,当某个线程调用了CyclicBarrier的await()方法后,该线程就进入了等待状态,而且计数器执行加1操作,当计数器的值达到了设置的初始值,调用await()方法进入等待状态的线程会被唤醒,继续执行各自后续的操作。CyclicBarrier在释放等待线程后可以重用,所以,CyclicBarrier又被称为循环屏障。

使用场景

可以用于多线程计算数据,最后合并计算结果的场景

CyclicBarrier与CountDownLatch的区别

  • CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法进行重置,并且可以循环使用
  • CountDownLatch主要实现1个或n个线程需要等待其他线程完成某项操作之后,才能继续往下执行,描述的是1个或n个线程等待其他线程的关系。而CyclicBarrier主要实现了多个线程之间相互等待,直到所有的线程都满足了条件之后,才能继续执行后续的操作,描述的是各个线程内部相互等待的关系。
  • CyclicBarrier能够处理更复杂的场景,如果计算发生错误,可以重置计数器让线程重新执行一次。
  • CyclicBarrier中提供了很多有用的方法,比如:可以通过getNumberWaiting()方法获取阻塞的线程数量,通过isBroken()方法判断阻塞的线程是否被中断。

代码示例

示例代码如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class CyclicBarrierExample {
 private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
 public static void main(String[] args) throws Exception {
 ExecutorService executorService = Executors.newCachedThreadPool();
 for (int i = 0; i < 10; i++){
 final int threadNum = i;
 Thread.sleep(1000);
 executorService.execute(() -> {
 try {
 race(threadNum);
 } catch (Exception e) {
 e.printStackTrace();
 }
 });
 }
executorService.shutdown();
 }
 private static void race(int threadNum) throws Exception{
 Thread.sleep(1000);
 log.info("{} is ready", threadNum);
 cyclicBarrier.await();
 log.info("{} continue", threadNum);
 }
}

设置等待超时示例代码如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
@Slf4j
public class CyclicBarrierExample {
 private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
 public static void main(String[] args) throws Exception {
 ExecutorService executorService = Executors.newCachedThreadPool();
 for (int i = 0; i < 10; i++){
 final int threadNum = i;
 Thread.sleep(1000);
 executorService.execute(() -> {
 try {
 race(threadNum);
 } catch (Exception e) {
 e.printStackTrace();
 }
 });
 }
 executorService.shutdown();
 }
 private static void race(int threadNum) throws Exception{
 Thread.sleep(1000);
 log.info("{} is ready", threadNum);
 try{
 cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
 }catch (BrokenBarrierException | TimeoutException e){
 log.warn("BarrierException", e);
 }
 log.info("{} continue", threadNum);
 }
}

在声明CyclicBarrier的时候,还可以指定一个Runnable,当线程达到屏障的时候,可以优先执行Runnable中的方法。

示例代码如下:

package io.binghe.concurrency.example.aqs;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Slf4j
public class CyclicBarrierExample {
 private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
 log.info("callback is running");
 });
 public static void main(String[] args) throws Exception {
 ExecutorService executorService = Executors.newCachedThreadPool();
 for (int i = 0; i < 10; i++){
 final int threadNum = i;
 Thread.sleep(1000);
 executorService.execute(() -> {
 try {
 race(threadNum);
 } catch (Exception e) {
 e.printStackTrace();
 }
 });
 }
 executorService.shutdown();
 }
 private static void race(int threadNum) throws Exception{
 Thread.sleep(1000);
 log.info("{} is ready", threadNum);
 cyclicBarrier.await();
 log.info("{} continue", threadNum);
 }
}

 

点击关注,第一时间了解华为云新鲜技术~

与带你熟悉3种AQS的线程并发工具的用法相似的内容:

带你熟悉3种AQS的线程并发工具的用法

摘要:AQS 的全称为(AbstractQueuedSynchronizer),AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器。 本文分享自华为云社区《【高并发】AQS中的CountDownLatch、Semaphore与CyclicBarrier核

通过4种经典应用,带你熟悉回溯算法

摘要:回溯的处理思想,有点类似枚举搜索。 本文分享自华为云社区《深入浅出回溯算法》,作者:嵌入式视觉。 一,如何理解回溯算法 深度优先搜索算法利用的就是回溯算法思想,但它除了用来指导像深度优先搜索这种经典的算法设计之外,还可以用在很多实际的软件开发场景中,比如正则表达式匹配、编译原理中的语法分析等。

[转帖]shell脚本之awk命令——按列求平均值、最大值、最小值

文章目录 写在前面awk求平均值awk求最大值awk求最小值awk求极值、均值的实际应用 写在前面 awk命令求极值和均值需要熟悉该命令的基本用法,如果你不熟悉该命令,请先阅读shell脚本之awk命令——分隔符介绍一文。本篇博文带你熟悉求平均值、最大值、最小值的方法,并以实际的应用带你进一步掌握a

一文带你读懂设计模式之责任链模式

翻了一下之前刚入职时候的学习笔记,发现之前在熟悉业务代码的时候曾经专门学习并整理过过设计模式中的责任链模式,之前只是对其简单了解过常用的设计模式有哪些,并未结合实例和源码深入对其探究,利用熟悉代码契机进行系统学习并整理文档如下。

带你彻底搞懂递归时间复杂度的Master公式

网上找到的Master公式推导过程都太过于复杂了,为此我特地找到一种小白也能看懂的推导过程。看完这篇文章后,你会对递归的时间复杂度深谙于心,打死都不会忘记。

带你读AI论文丨针对文字识别的多模态半监督方法

摘要:本文提出了一种针对文字识别的多模态半监督方法,具体来说,作者首先使用teacher-student网络进行半监督学习,然后在视觉、语义以及视觉和语义的融合特征上,都进行了一致性约束。 本文分享自华为云社区《一种针对文字识别的多模态半监督方法》,作者: Hint 。 摘要 直到最近,公开的真实场

带你动手做AI版的垃圾分类

摘要:本案例将使用YOLOX模型,实现一个简单的垃圾分类应用。 本文分享自华为云社区《ModelBox社区案例 - 使用YOLOX做垃圾分类》,作者:HWCloudAI。 1 ModelBox社区案例 - 使用YOLOX做垃圾分类 本案例将使用YOLOX模型,实现一个简单的垃圾分类应用,最终效果如下

带你认识JDK8中超nice的Native Memory Tracking

摘要:从 OpenJDK8 起有了一个很 nice 的虚拟机内部功能: Native Memory Tracking (NMT)。 本文分享自华为云社区《Native Memory Tracking 详解(1):基础介绍》,作者:毕昇小助手。 0.引言 我们经常会好奇,我启动了一个 JVM,他到底会

带你从0到1开发AI图像分类应用

摘要:通过一个垃圾分类应用的开发示例,介绍AI Gallery在AI应用开发流程中的作用。 本文分享自华为云社区《AI Gallery:从0到1开发AI图像分类应用》,作者: yd_269359708 。 现如今,人工智能(AI)技术在计算机领域内,得到了越来越广泛的重视,并在各行各业中得到应用。然

带你了解NLP的词嵌入

摘要:今天带领大家学习自然语言处理中的词嵌入的内容。 本文分享自华为云社区《【MindSpore易点通】深度学习系列-词嵌入》,作者:Skytier。 1 特征表示 在自然语言处理中,有一个很关键的概念是词嵌入,这是语言表示的一种方式,可以让算法自动的理解一些同类别的词,比如苹果、橘子,比如袜子、手