首页IT科技线程池和进程池的区别(带你熟悉3种AQS的线程并发工具的用法)

线程池和进程池的区别(带你熟悉3种AQS的线程并发工具的用法)

时间2025-08-01 19:52:50分类IT科技浏览5996
导读:摘要:AQS 的全称为(AbstractQueuedSynchronizer),AQS 是一个用来构建锁和同步器的框架,使用 AQS 能简单且高效地构造出应用广泛的大量的同步器。...

摘要: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()方法的方法签名如下:

使用场景

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

代码示例

调用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); } }

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

声明:本站所有文章    ,如无特殊说明或标注                     ,均为本站原创发布                。任何个人或组织                    ,在未征得本站同意时,禁止复制        、盗用          、采集                      、发布本站内容到任何网站            、书籍等各类媒体平台                     。如若本站内容侵犯了原著者的合法权益                 ,可联系我们进行处理       。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
fpgadds信号发生器 按键加频率 有上限(DDS结构的FPGA实现) 如何建立管道能带给你管道收益的建站项目!