JUC基础(五)—— 并发工具类

简介: JUC基础(五)—— 并发工具类

前言

之前我们已经讲解了不少JUC的内容,今天我们要讲解另一个方面们就是JUC提供的并发工具类


一、并发工具类用处

JUC内,并发工具包可以帮助开发者更方便地编写高效稳定的并发程序。其实,如果前面的知识你认真学了,尤其是关于AQS的部分内容,那么这些工具包的功能用户其实可以自行实现,但工具类的提供使得我们省的重复造轮子。


以下是JUC并发工具类的一些用途和说明,后面将会详细讲解

f03ac18d74ba471994349ba3a9df51f9.png


二、CountDownLatch

1. 用途

它可以让某个线程等待一个或多个线程完成操作后再执行,常用于控制一个或多个线程等待其他线程完成操作后再执行某个操作。例如,一个线程需要等待多个子线程全部执行完毕后再进行汇总计算,就可以使用CountDownLatch来实现


2. 示例

使用CountDownLatch阻塞主线程,其他线程执行 countDown() ,最后使得主线程恢复

public class CountdownLatchTest {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(3);
        // 定义初始度为3,即3次减度后将放开阻塞
        final CountDownLatch latch = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("子线程"+Thread.currentThread().getName()+"执行完成");
                        latch.countDown();//当前线程调用此方法,则计数减一
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            service.execute(runnable);
        }
        try {
            System.out.println("主线程"+Thread.currentThread().getName()+"等待子线程执行完成...");
            latch.await();//阻塞当前线程,直到计数器的值为0
            System.out.println("主线程"+Thread.currentThread().getName()+"开始执行...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3. 原理

800db8a7d84148d58fb1f087a04e7b99.png

主要是继承了AQS,其本质是一个互斥锁,只不过不同于一般的互斥锁,一般的互斥锁上锁解锁都是以 1 为单位,所以执行一次释放,就能唤醒后面的线程来竞争锁


而如上诉用例里,CountDownLatch 则一开始就上了一个力度为 3 的锁,这样当主线程使用await的时候实际是在申请锁,且只要当前状态不为0,就阻塞。而每次其他线程把这个数字减一,都会唤醒主线程,当然主线程又会来检测当前状态是否为0。如此反复,直到最后其真的为0,则竞争到锁,得以继续执行。因此,一个这种锁只能用一次。


三、Semaphore

1. 用途

它可以控制同时访问某个资源的线程数量,常用于控制并发请求的流量。例如,一个Web服务器同时只能处理有限的请求数量,就可以使用Semaphore来控制并发请求的数量。


2. 示例

public class SemaphoreDemo {
    // 可同时受理业务的窗口数量(同时并发执行的线程数)
    public static int threadTotal = 2;
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        for (int i = 0; i < 10; i++) {
            final int count = i;
            executorService.execute(() -> {
                try {
                    semaphore.acquire(1);
                    resolve(count);
                    semaphore.release(1);
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        executorService.shutdown();
    }
    private static void resolve(int i) throws InterruptedException {
        log.info("服务号{},受理业务中。。。", i);
        Thread.sleep(2000);
    }
}

3. 原理

bfedd198bdf9428c89cbf31ef8e675df.png

主要是继承了AQS,其本质是一个共享锁,只不过不同于普通共享锁不做数量限制,本共享锁初始值是一个共享额度,当一个线程申请后,少于额度时则阻塞本线程。当一个线程释放本额度时,则唤醒所有线程,所以这里还可以选择是否使用公平锁


如实例,设置了共享额度为2,则每一个进来的线程会将值减1,当第三个线程进入时,只能阻塞。等到有线程释放额度时,它就能被唤醒,进而竞争这个额度了。


四、CyclicBarrier

1. 用途

它可以让一组线程互相等待,直到所有线程都到达一个屏障点后再一起执行,常用于多线程计算结果的合并。例如,多个分布式计算节点需要将自己的计算结果合并到一起,就可以使用CyclicBarrier来实现。


2. 示例

public class CyclicBarrierTest {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        //周末3人聚会,需要等待3个人全部到齐餐厅后才能开始吃饭
        CyclicBarrier cb = new CyclicBarrier(3);
        System.out.println("初始化:有" + (3 - cb.getNumberWaiting()) + "个人正在赶来餐厅");
        for (int i = 0; i < 3; i++) {
            //设置用户的编号
            final int person = i;
            executor.execute(() -> {
                try {
                    Thread.sleep((long) (Math.random() * 10000));
                    System.out.println(Thread.currentThread().getName() + "---用户" + person + "即将达到餐厅," +
                            "用户" + person + "到达餐厅了。" + "当前已有" + (cb.getNumberWaiting() + 1) + "个人到达餐厅");
                    cb.await();
                    System.out.println("三个人都到到餐厅啦," + Thread.currentThread().getName() + "开始吃饭了");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();    //关闭线程池
    }
}


3. 原理

CyclicBarrier相对于其他并发工具类来说,要复杂一点,它没有重写AQS的代码,它的阻塞是也并非像上述两者一样是靠阻塞队列实现线程阻塞,而是依赖条件队列的阻塞:当某个条件满足,则唤醒条件队列里所有的线程。而且它还有 执行额外任务和可重置 的特点,我们看它的构造函数

    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        // 利用两个数存阈值,一个数每次递减,另一个数不变,方便重置时恢复原值
        this.parties = parties;
        this.count = parties;
        // 入参包含可运行代码段,达到条件即可执行
        this.barrierCommand = barrierAction;
    }

而其主要代码,我们也稍微讲解下

3a522090752e42f8946999889d9b5439.png


逻辑其实并不复杂,只是其实现比较不一样罢了


五、总结

  • CountDownLatch : 自己一个人阻塞,要等指定的人数帮助后恢复(只能用一次)
  • Semaphore : 固定通道大小,只允许指定人数进入
  • CyclicBarrier :来一个阻塞一个,最后阻塞到指定人数后,所有阻塞的一起恢复

cf5fb89119684d608c98adb5403f836b.png


目录
相关文章
|
9月前
并发的三个工具类
并发的三个工具类
24 0
|
2月前
|
缓存 安全 Java
Java的线程池与并发工具类技术性文章
Java的线程池与并发工具类技术性文章
13 0
|
2月前
|
缓存 算法 安全
Java并发编程学习8-同步工具类
【4月更文挑战第2天】本篇介绍一下Java平台类库下的常用的同步工具类(闭锁、Future、信号量和栅栏)
28 2
Java并发编程学习8-同步工具类
|
2月前
|
安全 Java
深入理解 Java 多线程和并发工具类
【4月更文挑战第19天】本文探讨了Java多线程和并发工具类在实现高性能应用程序中的关键作用。通过继承`Thread`或实现`Runnable`创建线程,利用`Executors`管理线程池,以及使用`Semaphore`、`CountDownLatch`和`CyclicBarrier`进行线程同步。保证线程安全、实现线程协作和性能调优(如设置线程池大小、避免不必要同步)是重要环节。理解并恰当运用这些工具能提升程序效率和可靠性。
|
12月前
|
存储 安全 算法
一天一个 JUC 工具类 -- 并发集合
使用JUC工具包中的并发集合,我们可以避免手动处理锁和同步的复杂性,从而降低出现线程安全问题的概率。这些并发集合通过内部采用高效的算法和数据结构来优化并发操作,从而提供更好的性能和扩展性。
|
2月前
|
安全 Java 调度
【多线程】Java如何实现多线程?如何保证线程安全?如何自定义线程池?
【多线程】Java如何实现多线程?如何保证线程安全?如何自定义线程池?
218 0
|
9月前
|
Java 程序员 调度
JUC第三讲:Java 并发-线程基础
JUC第三讲:Java 并发-线程基础
|
11月前
|
安全 Java 开发者
【Java|多线程与高并发】JUC中常用的类和接口
JUC是Java并发编程中的一个重要模块,全称为Java Util Concurrent(Java并发工具包),它提供了一组用于多线程编程的工具类和框架,帮助开发者更方便地编写线程安全的并发代码。
|
Java 关系型数据库 MySQL
【Java并发编程 十】JUC并发包下的工具类
【Java并发编程 十】JUC并发包下的工具类
125 0
|
存储 安全 Java
【JUC基础】11. 并发下的集合类
我们直到ArrayList,HashMap等是线程不安全的容器。但是我们通常会频繁的在JUC中使用集合类,那么应该如何确保线程安全?

热门文章

最新文章

  • 1
    流量控制系统,用正则表达式提取汉字
    27
  • 2
    Redis09-----List类型,有序,元素可以重复,插入和删除快,查询速度一般,一般保存一些有顺序的数据,如朋友圈点赞列表,评论列表等,LPUSH user 1 2 3可以一个一个推
    27
  • 3
    Redis08命令-Hash类型,也叫散列,其中value是一个无序字典,类似于java的HashMap结构,Hash结构可以将对象中的每个字段独立存储,可以针对每字段做CRUD
    27
  • 4
    Redis07命令-String类型字符串,不管是哪种格式,底层都是字节数组形式存储的,最大空间不超过512m,SET添加,MSET批量添加,INCRBY age 2可以,MSET,INCRSETEX
    28
  • 5
    S外部函数可以访问函数内部的变量的闭包-闭包最简单的用不了,闭包是内层函数+外层函数的变量,简称为函数套函数,外部函数可以访问函数内部的变量,存在函数套函数
    25
  • 6
    Redis06-Redis常用的命令,模糊的搜索查询往往会对服务器产生很大的压力,MSET k1 v1 k2 v2 k3 v3 添加,DEL是删除的意思,EXISTS age 可以用来查询是否有存在1
    31
  • 7
    Redis05数据结构介绍,数据结构介绍,官方网站中看到
    22
  • 8
    JS字符串数据类型转换,字符串如何转成变量,+号只要有一个是字符串,就会把另外一个转成字符串,- * / 都会把数据转成数字类型,数字型控制台是蓝色,字符型控制台是黑色,
    20
  • 9
    JS数组操作---删除,arr.pop()方法从数组中删除最后一个元素,并返回该元素的值,arr.shift() 删除第一个值,arr.splice()方法,删除指定元素,arr.splice,从第一
    21
  • 10
    定义好变量,${age}模版字符串,对象可以放null,检验数据类型console.log(typeof str)
    20