多线程场景的学习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;
    }
}
相关推荐
island13147 小时前
CANN GE(图引擎)深度解析:计算图优化管线、内存静态规划与异构 Stream 调度机制
c语言·开发语言·神经网络
云姜.7 小时前
线程和进程的关系
java·linux·jvm
是码龙不是码农7 小时前
支付防重复下单|5 种幂等性设计方案(从初级到架构级)
java·架构·幂等性
曹牧7 小时前
Spring Boot:如何在Java Controller中处理POST请求?
java·开发语言
heartbeat..7 小时前
JVM 性能调优流程实战:从开发规范到生产应急排查
java·运维·jvm·性能优化·设计规范
浅念-7 小时前
C++入门(2)
开发语言·c++·经验分享·笔记·学习
WeiXiao_Hyy7 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
User_芊芊君子7 小时前
CANN010:PyASC Python编程接口—简化AI算子开发的Python框架
开发语言·人工智能·python
苏渡苇7 小时前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
团子的二进制世界7 小时前
G1垃圾收集器是如何工作的?
java·jvm·算法