多线程并发之CountDownLatch阻塞等待

简介: 多线程并发之CountDownLatch阻塞等待

1. 简介


CountDownLatch中count down是倒数的意思,latch则是门闩、锁住的含义。整体含义可以理解为倒数的门栓。CountDownLatch的作用也是如此,在构造CountDownLatch的时候需要传入一个整数n(必须>0),在这个整数“倒数”到0之前,主线程需要等待在门口,而这个“倒数”过程则是由各个执行线程驱动的,每个线程执行完一个任务“倒数”一次。总结来说,CountDownLatch的作用就是等待其他的线程都执行完任务,必要时可以对各个任务的执行结果进行汇总,然后主线程才继续往下执行。


CountDownLatch主要有两个方法:countDown()和await()。countDown()方法用于使计数器减一,其一般是执行任务的线程调用,await()方法则使调用该方法的线程处于等待状态,其一般是主线程调用。这里需要注意的是,countDown()方法并没有规定一个线程只能调用一次,当同一个线程调用多次countDown()方法时,每次都会使计数器减一;另外,await()方法也并没有规定只能有一个线程执行该方法,如果多个线程同时执行await()方法,那么这几个线程都将处于等待状态,并且以共享模式享有同一个锁。


2. 方法API


方法:

方法 说明
await() 使当前线程进入同步队列进行等待,直到latch的值被减到0或者当前线程被中断,当前线程就会被唤醒。
await(long timeout, TimeUnit unit) 等待timeout时间后,count的值还不是0,不再等待,那么将继续执行
countDown() 使latch的值减1,如果减到了0,则会唤醒所有等待在这个latch上的线程。
getCount() 获得latch的数值。


3. 使用


3.1 await()


示例:

CountDownLatch count = new CountDownLatch(3);
        new Thread(()->{
            //处理业务1
            try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } finally {
                count.countDown();//确保每个任务执行完递减
            }
        }, "t1").start();
        new Thread(()->{
            //处理业务2
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } finally {
                count.countDown();//确保每个任务执行完递减
            }
        }, "t2").start();
        new Thread(()->{
            //处理业务3
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } finally {
                count.countDown(); //确保每个任务执行完递减
            }
        }, "t3").start();
        long startTime = System.currentTimeMillis();
        count.await(); // 等待任务执行
        long endTime = System.currentTimeMillis();
        System.out.println("任务执行完成,耗时:" + (endTime - startTime) + "毫秒");


结果:

1673437659630.jpg


3.2 boolean await(long timeout, TimeUnit unit)


boolean await(long timeout, TimeUnit unit)示例:

CountDownLatch count = new CountDownLatch(3);
        new Thread(()->{
            //处理业务1
            try { TimeUnit.SECONDS.sleep(1);
                System.out.println("task1 over");} catch (InterruptedException e) { e.printStackTrace(); } finally {
                count.countDown();//确保每个任务执行完递减
            }
        }, "t1").start();
        new Thread(()->{
            //处理业务2
            try { TimeUnit.SECONDS.sleep(2);
                System.out.println("task2 over");} catch (InterruptedException e) { e.printStackTrace(); } finally {
                count.countDown();//确保每个任务执行完递减
            }
        }, "t2").start();
        new Thread(()->{
            //处理业务3
            try { TimeUnit.SECONDS.sleep(3);
                System.out.println("task3 over");} catch (InterruptedException e) { e.printStackTrace(); } finally {
                count.countDown(); //确保每个任务执行完递减
            }
        }, "t3").start();
        long startTime = System.currentTimeMillis();
        boolean await = count.await(2, TimeUnit.SECONDS);// 指定等待时间,如果当前有任务未执行完成则返回false
        System.out.println("所有任务是否执行完成:" + (await ? "是" : "否"));
        System.out.println("计数器值为:" + count.getCount());
        long endTime = System.currentTimeMillis();
        System.out.println("任务执行完成,耗时:" + (endTime - startTime) + "毫秒");


分析:


开启三个线程去执行任务,任务1、任务2、任务3耗时依次为1s、2s、3s

计数器await等待2s,如果2s后计数器值不为0(即三个任务中有任务未执行完成),那么就返回false。可以用在一些比较耗时长的任务上,例如调用第三方接口、业务线比较长,当超过指定时间后就当作失败处理,避免服务一直处于等待阻塞状态。


结果:

1673437688730.jpg


4. CountDownLatch和Thread.join()方法的区别


1、CountDownLatch的作用就是允许一个或多个线程等待其他线程完成操作,看起来有点类似join() 方法,但其提供了比join()更加灵活的API。


2、CountDownLatch可以手动控制在n个线程里调用n次countDown()方法使计数器进行减一操作,也可以在一个线程里调用n次执行减一操作。 而 join() 的实现原理是不停检查join线程是否存活,如果join 线程存活则让当前线程永远等待。所以两者之间相对来说还是CountDownLatch使用起来较为灵活。


5. CountDownLatch的不足


CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。


6. 扩展


如果采用多线程异步任务Future,通过CompletableFuture.allOf也可以实现同样的效果,阻塞等待任务执行结果,参考文章多线程Future,CompletableFuture

相关文章
|
1月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
155 0
|
23天前
|
安全
List并发线程安全问题
【10月更文挑战第21天】`List` 并发线程安全问题是多线程编程中一个非常重要的问题,需要我们认真对待和处理。只有通过不断地学习和实践,我们才能更好地掌握多线程编程的技巧和方法,提高程序的性能和稳定性。
130 59
|
14天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
1月前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
27 1
|
1月前
|
Java C++
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
【多线程】JUC的常见类,Callable接口,ReentranLock,Semaphore,CountDownLatch
33 0
|
2月前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
2月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
59 0
|
2月前
|
C语言
C语言 网络编程(九)并发的UDP服务端 以线程完成功能
这是一个基于UDP协议的客户端和服务端程序,其中服务端采用多线程并发处理客户端请求。客户端通过UDP向服务端发送登录请求,并根据登录结果与服务端的新子线程进行后续交互。服务端在主线程中接收客户端请求并创建新线程处理登录验证及后续通信,子线程创建新的套接字并与客户端进行数据交换。该程序展示了如何利用线程和UDP实现简单的并发服务器架构。
|
1月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
43 1
C++ 多线程之初识多线程
|
25天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
19 3

热门文章

最新文章