多线程场景的学习3,使用CountDownLatch

1、CountDownLatch的用法

其核心机制是通过一个计数器实现线程等待,计数归零的时候,释放所有线程。

创建计数器:创建时指定正整数(如 N),表示需要等待的线程数量或任务数。

子线程完成任务后调用 countDown(),计数器减 1。

主线程调用 await() 阻塞,直到计数器归零后才会继续执行主线程代码。⁠⁣ ⁠⁣

2、CountDownLatch 和 Future的get方法区别

Future的get方法也可以实现线程等待,但它是针对单个任务(线程),且get方法是顺序阻塞的。比如,有多个任务,调用get方法会顺序获取,就算第二个线程早就执行完了,也会等待第一个Future的get方法拿到返回之后,才会执行第二个Future的get方法。

而CountDownLatch是针对一组任务,他不关心具体线程,只重视一组任务(线程)的结果。

3、场景

假设,预订三间房间。从 【预订】 --> 【预定成功/失败】,每一间房肯定有不少的校验。最后,全部预订成功了,才会发送邮件,有一间房预订失败了,就发送具体失败哪一间房。

这个时候,就可以使用 CountDownLatch ,将【预订】到【预定成功/失败】的这个流程单独弄成异步的,然后主流程等待异步线程全部结束,在执行发邮件的流程。

4、代码实现

返回结果定义

java 复制代码
public class BookRoomResult {
    private String success;
    private String roomId; //房间号(唯一键)
    private String errorMsg;

    public BookRoomResult(String success, String roomId, String errorMsg) {
        this.success = success;
        this.roomId = roomId;
        this.errorMsg = errorMsg;
    }

    public String getSuccess() {
        return success;
    }

    public void setSuccess(String success) {
        this.success = success;
    }

    public String getRoomId() {
        return roomId;
    }

    public void setRoomId(String roomId) {
        this.roomId = roomId;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}

多线程模拟

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

public class ThreadPoolExecutorDemo1 {
    //核心线程数,核心线程默认会一直存在
    public static int corePoolSize = 5;

    //最大线程数
    public static int maximumPoolSize = 15;

    //非核心线程的线程存活时间
    public static int keepAliveTime = 2;


    public static void main(String[] args) {
        ThreadPoolExecutor executor = buildThreadPool();
        List<String> room = Arrays.asList("room1", "room2", "room3");

        //有多少间房,就创建多大的计数器
        CountDownLatch latch = new CountDownLatch(room.size());

        List<Future<BookRoomResult>> result = new ArrayList<>();
        for (int i = 0; i < room.size(); i++) {
            try {
                int finalI = i;
                Future<BookRoomResult> submit = executor.submit(new Callable() {
                    @Override
                    public BookRoomResult call() {
                        try {
                            if (finalI == 2) {
                                Thread.sleep(2000);
                                return new BookRoomResult("fail", room.get(finalI), "预订失败,客房满了");
                            }
                        } catch (Exception e) {
                            System.out.println("异步处理发生未知异常");
                        } finally {
                            //每个线程内的任务执行完毕后,计数器减 1
                            latch.countDown();
                        }
                        return new BookRoomResult("success", room.get(finalI), "预订成功");
                    }
                });
                result.add(submit);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        try {
            //主线程阻塞10s,等待计数器归零
            //10秒内如果异步线程全部执行完毕了,那就走正常流程
            if (latch.await(10, TimeUnit.SECONDS)) {
                //到这一步了,说明每个线程的任务执行完毕,用get方法肯定是不会出现阻塞
                for (Future<BookRoomResult> bookRoom : result) {
                    if ("fail".contains(bookRoom.get().getSuccess())) {
                        System.out.println("存在失败房间:" + bookRoom.get().getRoomId() + "失败原因:" + bookRoom.get().getErrorMsg());
                    }
                }
            } else {
                System.out.println("10s内没有收到全部的异步线程返回,疑似网络出问题");
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        System.out.println("==================主线程执行完毕===================");
    }

    /**
     * 定义一个线程池
     *
     * @return
     */
    public static ThreadPoolExecutor buildThreadPool() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(10), //有界队列,容量10
                Executors.defaultThreadFactory(),
                (r, executor1) -> System.out.println("线程超过最大线程数"));
        return executor;
    }
}
相关推荐
云烟成雨TD14 小时前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Wenweno0o14 小时前
0基础Go语言Eino框架智能体实战-chatModel
开发语言·后端·golang
于慨14 小时前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
swg32132114 小时前
Spring Boot 3.X Oauth2 认证服务与资源服务
java·spring boot·后端
gelald14 小时前
SpringBoot - 自动配置原理
java·spring boot·后端
殷紫川14 小时前
深入理解 AQS:从架构到实现,解锁 Java 并发编程的核心密钥
java
一轮弯弯的明月14 小时前
贝尔数求集合划分方案总数
java·笔记·蓝桥杯·学习心得
chenjingming66615 小时前
jmeter线程组设置以及串行和并行设置
java·开发语言·jmeter
殷紫川15 小时前
深入拆解 Java volatile:从内存屏障到无锁编程的实战指南
java
eddieHoo15 小时前
查看 Tomcat 的堆内存参数
java·tomcat