《重学Java高并发》同步转异步编程技巧与实战运用

简介: 《重学Java高并发》同步转异步编程技巧与实战运用

1、线程池+Future模式


笔者在公司中负责开发某一个产品时,需要实现一个告警模块,告警通知方式需要为钉钉群、电话短信等方式,并且及时时单一的告警方式,例如钉钉群告警,也需要同时发送到多个群(监控中心、业务项目组钉钉群),使监控告警能真正通知到各个相关方,确保人工及时处理跟进,避免事态进一步发展。


发送钉钉群告警信息的时序图如下:

8cde7b9f96dbd008e88b05cbee1595f7.png

发送到不同的钉钉群,这个过程完成可以并发,并发同步等待发送结果,即这个过程是一个同步场景,但可以转化成异步(多并发),即经典的同步转异步


要实现这个功能,我想大家第一时间会想到使用线程池+Future模式。

0502d7feda738c10f599cc7b7c5f618f.png

代码实现的关键点如下:


  • 创建一个线程池,请大家一定要使用自定义线程工厂,为创建的线程命名,如代码@2.
  • 两个任务,提交到线程池中,此过程是一个异步。注意:SendDingDingTalk是实现java.util.concurrent.Callable,即带返回值的任务。
  • 提交到线程池中的任务如果实现了java.util.concurrent.Callable,提交到线程池返回一个Future对象(凭证),通过调用Feture对象的get()方法,如果任务未完成,则会阻塞,即实现同步转异步调用,再转同步的调用效果,从而提高并发,提高性能。


2、CountDownLatch的妙用


使用线程池+Future模式,有时候会显得比较笨重,因为需要额外创建一个线程池,如果在一些轻量级场景(单线程+批量)场景下也希望将同步转异步,我们有其他办法没?


使用CountDownLatch!!!


接下来我们结合场景来说说如何使用CountDownLatch。


例如我们在对数据库数据进行清理时,通常会将数据进行分页(任务分批),然后创建多个子线程,主线程将任务分批后提交到子线程,等待子线程全部执行完成后,主线程打印执行日期,其时序图如下所示:

a303d5131b6c987e976ace315eee2e3d.png

主线程如何得知子线程执行完毕呢?在java中,通常的方案是 join,但juc框架中的CountDownLatch实现了与Thread.join相同的语义,其伪代码实现如下:

97df1a14c20d3eae7c45a81158770fd7.png

使用CountDownLatch的要点如下:


  • 首先会创建一个CountDownLatch,并且指定计数器,通常为子线程的个数。
  • 然后主线程向子线程提交任务,并且子线程在完成自己的工作后,调用CountDownLatch的countDown,计数器降一,“以此来表示子线程已处理完毕”。
  • 各个子线程异步执行,但最终还是要转为同步,因为主线程需要等待结果,故主线程需要调用CountDownLatch的await方法,进行阻塞,直到计数器为0。


关键中的关键:引入多个子线程并发执行,将同步任务转换为异步执行,但最终结果是必须等待所有子线程运行完毕,故此时异步又需要转回同步:


即CountDownLatch通过引入计数器以及countDown()方法与await()方法实现线程之间的协作,从而实现同步转异步。

相关文章
|
5天前
|
存储 监控 安全
JVM工作原理与实战(十六):运行时数据区-Java虚拟机栈
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了运行时数据区、Java虚拟机栈等内容。
11 0
|
6天前
|
Java
Java中的多线程编程:基础知识与实战技巧
【5月更文挑战第6天】多线程编程是Java中的一个重要特性,它允许我们在一个程序中同时执行多个任务。本文将介绍Java多线程的基础知识,包括线程的创建、启动、同步和通信,以及如何在Java中实现多线程编程。通过实例代码和解析,帮助读者深入理解Java多线程编程的概念和应用。
|
11天前
|
存储 Java 数据格式
Java实战:轻松掌握文件重命名与路径提取技巧
Java实战:轻松掌握文件重命名与路径提取技巧
18 0
|
12天前
|
设计模式 算法 安全
Java多线程编程实战:从入门到精通
【4月更文挑战第30天】本文介绍了Java多线程编程的基础,包括线程概念、创建线程(继承`Thread`或实现`Runnable`)、线程生命周期。还讨论了线程同步与锁(同步代码块、`ReentrantLock`)、线程间通信(等待/通知、并发集合)以及实战技巧,如使用线程池、线程安全设计模式和避免死锁。性能优化方面,建议减少锁粒度和使用非阻塞算法。理解这些概念和技术对于编写高效、可靠的多线程程序至关重要。
|
13天前
|
XML Java 测试技术
Java异常处理神器:Guava Throwables类概念与实战
【4月更文挑战第29天】在Java开发中,异常处理是保证程序稳定性和可靠性的关键。Google的Guava库提供了一个强大的工具类Throwables,用于简化和增强异常处理。本篇博客将探讨Throwables类的核心功能及其在实战中的应用。
27 2
|
Java
java中同步嵌套引起的死锁事例代码
/* 目的:自己写一个由于同步嵌套引起的死锁! 思路:多个线程在执行时,某一时刻,0-Thread绑定了LockA锁,1-Thread绑定了LockB锁! 当0-Thread要去绑定LockB锁时 和 1-Thread要去绑定LockA锁时都不能绑定,此时两个线程不能继续进行! *...
504 0
|
1天前
|
Java
Java一分钟:线程协作:wait(), notify(), notifyAll()
【5月更文挑战第11天】本文介绍了Java多线程编程中的`wait()`, `notify()`, `notifyAll()`方法,它们用于线程间通信和同步。这些方法在`synchronized`代码块中使用,控制线程执行和资源访问。文章讨论了常见问题,如死锁、未捕获异常、同步使用错误及通知错误,并提供了生产者-消费者模型的示例代码,强调理解并正确使用这些方法对实现线程协作的重要性。
10 3
|
1天前
|
安全 算法 Java
Java一分钟:线程同步:synchronized关键字
【5月更文挑战第11天】Java中的`synchronized`关键字用于线程同步,防止竞态条件,确保数据一致性。本文介绍了其工作原理、常见问题及避免策略。同步方法和同步代码块是两种使用形式,需注意避免死锁、过度使用导致的性能影响以及理解锁的可重入性和升级降级机制。示例展示了同步方法和代码块的运用,以及如何避免死锁。正确使用`synchronized`是编写多线程安全代码的核心。
34 2

热门文章

最新文章