您的位置:首页 > 娱乐 > 八卦 > 凡科商城是什么_郑州建筑工程有限公司_百度云怎么找资源_西安seo服务公司排名

凡科商城是什么_郑州建筑工程有限公司_百度云怎么找资源_西安seo服务公司排名

2024/10/7 2:03:39 来源:https://blog.csdn.net/qq_31156277/article/details/142725924  浏览:    关键词:凡科商城是什么_郑州建筑工程有限公司_百度云怎么找资源_西安seo服务公司排名
凡科商城是什么_郑州建筑工程有限公司_百度云怎么找资源_西安seo服务公司排名

前言:如果不掌握多线程,那么还谈不上是一位合格的 Java 开发工程师

回顾过往,发现我是如此的喜欢使用 CountDownLatch,它带给我便捷、速度、也带给我谨慎。今天就来盘一盘这个爱不释手的 CountDownLatch。

一、"慢是原罪"

😊 最简单、最可靠的编程就是不要使用多线程,但同时系统的某些功能运行也会因此慢得不能忍受!

认识 CountDownLatch 这个核心工具类,还得从以下几个故事开始。

1.1 故事一:同步任务

同步作业,从系统 A,同步到系统 B, 主要同步的内容为:部门 + 人员

图片

注:部门和人员的信息来源不同的 API,需要分成两个接口同步,且先同步部门再同步人员,并且部门要构造成一棵树,人员信息没有批量接口。

伪代码逻辑

  1. 先同步整个部门树

  2. 再串行同步每个部门树上的人员信息, 人员信息的获取和写入都依赖部门ID。

逻辑清晰,代码自然很快实现。但跑真实数据的时候,才发现整个过程超级慢,同步每个部门和人员的接口都是 http ,数量一多,调用次数也就多,耗时自然就高。

结果就几万人,同步花了两小时虽然说是让机器自己跑,但时间太长,不符合我的要求!

由于单个部门人员信息的获取和写入不存在先后依赖,可以并行。最后用 CountDownLatch 改造,时间缩短 10 倍。

1.2 故事二:导出数据

我们的交付老师(非技术出身)找到我,说系统有一个导出任务,当数量少的时候,可以导出成功,但是当任务多的时候总是导出失败,根本导不出来。

有时候抽象的语言,需要用图来做证明,当我们看到图就知道所描述的内容是什么,真正是“一图胜千言”

我追问了一句,导不出来是什么现象?

图片

她说了就是不行,然后把电脑拿过来,导出五千多条数据,点击导出,等待快到 2 分钟的时候,请求中断。(串行获取所有数据,然后写入 Excel 中,由于串行过程耗时过长,连接断开

由于导出的数据,每一条都需要请求 RPC 补充完整数据,由于串行执行,肯定比较耗时。最后用 CountDownLatch 进行改造。先使用 CountDownLatch 进行多线程数据组装,然后再写入 Excel 中。

备注:由于数量上限为 1 w,因此允许这么做,当数据量太大时,即使用 CountDownLatch 也不好使。最好的方式还是需要走离线调度,但也会失去一定的实时性。CountDownLatch 的改造不是一个最佳方法,算是一个简单的折中解决方式。

1.3 故事三:首页加载太慢

首页加载太慢,需要几秒,有一个聚合等待逻辑比较耗时。其中有一个数据需要其他几个数据计算出来以后,才能计算。

图片

系统运行一段时间,开始变得慢起来, 检查发现请求接口串行。

使用 CountDownLatch 将依赖的数据并行计算。

1.4 故事四:更新丢失

写出数据,依赖前面的更新数据。由于更新数据较多,采用多线程,但是并没等每个任务都更新结束。因此存在部分数据不符合预期。(多线程执行更新,但是没有等待执行结果就进入下一步)

使用 CountDownLatch,等所有数据更新结束,再读取。显然这是非常典型的依赖。

故事最后:我们没有批评和抨击别人的权利,但是我们要有解决问题的实际能力!

二、依赖等待是本质

CountDownLatch 属于线程之间的通信,或者线程之间的协作,但我认为它解决的是多任务依赖等待问题

2.1 案例描述

如下图所示:ABC 都是繁重的 http/RPC 接口,执行完毕才能执行 D,因此整个执行时间,随着 ABC 耗时增加而增加。但是并行任务,则取 ABC 的最大值即可。因此在耗时上来讲,串行属于时间和,并行属于时间极值

图片

2.2 场景总结

那么哪些场景可以使用 CountDownLatch。简言之:有多任务执行结果的依赖都可以考虑 CountDownLatch

图片

三、认识 CountDownLatch

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes(一个同步辅助工具,允许一个或多个线程等待其他线程执行的一组操作完成)

CountDownLatch 初始化一个给定值,调用 await 方法阻塞,直到当前计数由于调用 countDown 方法而达到零,

之后所有等待的线程都会被释放。

3.1 CountDownLatch 接口

CountDownLatch 源码给出了两个使用案例。(只是简单 demo,实践在使用上我们更应该考虑异常)

图片

CountDownLatch 的关键方法:

  • CountDownLatch(int count):构造方法,初始化计数器的值。

  • countDown():每调用一次,计数器减一。当计数器的值达到零时,所有等待的线程将被唤醒。

  • await():调用此方法的线程将被阻塞,直到计数器的值为零。

  • await(long timeout, TimeUnit unit):超时机制的等待方法。

3.2 注意事项

注意事项:countDownLatch 的数量减到 0, 否则会存在一直等待,因为其他线程没有办法继续获取锁。这也是十分危险的事情。

无法执行 countDown(), 导致其他等待线程无法执行。例异常时,不能执行到该段代码一样。

可以将await() 替换成await(long timeout, TimeUnit unit) 带有超时机制的等待方法,避免特殊情况的等待。

3.3  替代 CompletableFuture 工具类

可以使用 CompletableFuture 这些 API 来替换 CountDownLatch 的功能。CompletableFuture 提供了较多的 API 来替代 CountDownLatch。处理异常时特别方便的。

  • 组合多个异步任务:使用 CompletableFuture.allOf() 可以等待多个 CompletableFuture 任务完成。这类似于 CountDownLatch,但是更灵活,因为它可以等待任何数量的 CompletableFuture 任务。

  • 异常处理CompletableFuture 可以通过 exceptionally() 方法来处理异步任务中的异常,而 CountDownLatch 需要你在 countDown() 之前手动处理。

  • 回调CompletableFuture 可以使用 whenComplete() 或 handle() 方法来添加回调,这些回调会在异步任务完成时执行。

当然 CompletableFuture 还有很多高级 API,可以根据场景选择合适的 API。虽然 CompletableFuture 很好用,但是我也建议你尝试用用 CountDownLatch

3.4 CountDownLatch 部分源码

CountDownLatch 的代码很少,主要复用了 AbstractQueuedSynchronizer (AQS)的能力。

CountDownLatch 的内部类 Sync 继承自 AbstractQueuedSynchronizer,重写了 tryAcquireShared 和 tryReleaseShared 方法来实现同步逻辑。tryAcquireShared 方法用于判断是否可以获取共享锁(即计数器是否为零),而 tryReleaseShared 方法用于减少计数器并唤醒等待的线程。

  • countDown() 方法通过调用 AQS 的 releaseShared 方法来减少计数器,并在计数器为零时唤醒所有等待的线程。

  • await() 方法通过调用 AQS 的 acquireSharedInterruptibly 方法来阻塞当前线程,直到计数器为零或线程被中断。

   private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;Sync(int count) {setState(count);}int getCount() {return getState();}// ===== 实现 tryAcquireShared 和 tryReleaseShared 两个方法 ===// 这个方法是用来尝试获取共享模式下的同步状态。// 在 CountDownLatch 的使用场景中,当一个或多个线程调用 await() 方法时,// 它们会尝试获取共享锁。tryAcquireShared 方法的实现逻辑是检查 AQS 同步状态//(即 CountDownLatch 的计数器)是否为0protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}// 这个方法是用来尝试释放共享模式下的同步状态。// 当线程调用 countDown() 方法时,实际上是在减少 CountDownLatch 的计数器/*1. 通过循环,获取当前同步状态(计数器值)。2. 如果计数器已经为0,说明已经没有需要等待的事件了,方法返回false,不再执行任何操作。3. 如果计数器不为0,方法会尝试通过 CAS(比较并交换)操作将计数器减1。4. 如果 CAS 操作成功,并且新的计数器值为0,表示所有事件都已经完成,方法返回true,此时 AQS 会释放所有等待的线程。*/protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)return false;int nextc = c-1;if (compareAndSetState(c, nextc))return nextc == 0;}}}

为什么实现这两个方法

tryAcquireShared 和 tryReleaseShared 是为了定义 CountDownLatch 特定的同步逻辑:

  1. tryAcquireShared: 确定调用 await() 的线程是否应该被阻塞。如果是在计数器还没减到 0 的时候调用,那么线程应该被阻塞。

  2. tryReleaseShared: 定义当一个事件完成时(调用 countDown()),计数器如何减少,并在所有事件都完成时(计数器为0)释放所有等待的线程。

这两个方法是 AQS 框架中的钩子方法,允许开发者定义自己的同步逻辑,以实现不同的同步工具,如 CountDownLatchSemaphoreReentrantLock 等。通过这种方式,CountDownLatch 能够提供一种机制,让一个或多个线程等待直到其他线程执行完毕某些操作。

四、结束语

4.1 面试经历

曾经被大厂的面试官问过是否使用过 CountDownLatch,当然也当过面试官考核过别人。(历史总是这样惊人地相似)

图片

考核的背后原因:

校招同学:是否知道 CountDownLatch 这个知识点,有没有进一步了解原理。以考核知识点,技术钻研度为主。

社招同学:有没有使用经验,能不能遇到 CountDownLatch 场景能够合理正确地使用。以考核实际应用为主。

特别说明:面试并不是说让你背下多少源码,也并不需要这么做。更应该理解源码后,梳理出原理、流程。不可本末倒置,以为理解源码就是背诵和记忆源码,并不是。

4.2 CountDownLatch 总结

如何介绍 CountDownLatch,可以从功能理解、应用场景、使用经验和注意事项

功能介绍: Java 中的一个同步辅助类,它允许一个或多个线程等待一组操作完成;线程之间的协作!

场景经验:前后依赖任务,前面属于多任务,可并行执行。比如部门人员同步、数据聚合等待、异步数据获取返回。注意需要保证 countDown() 方法的调用,特别注意在异常情况

部分源码:使用上的两个核心方法:countDown() 和 await()/await(...);  内部继承 AbstractQueuedSynchronizer (AQS) 作为其同步器。并继承重写 tryAcquireShared/tryReleaseShared 作为判断获取公平锁和释放锁的关键

4.3 一些感想

以前觉得这个 countDownLatch 没有太大的作用,直到在一些场景后实践后才知道它是有多么的好用,但是也有很多注意事项,要非常谨慎地处理。(在使用任何线程工具的时候,都需要记住异常、边界问题的处理

每一次换工作都免不了要做一些重复的事情,希望这是最后一次!毕竟重复的事情是浪费生命!

从上一家离职后所有资料都回收了,可惜好多代码和文档都不能拿走,能带走就只有思路了。

图片

好了,本文到此结束。

已同步发布公众号:面汤放盐 闲谈一下 CountDownLatch (欢迎关注)

掘金技术:https://juejin.cn/post/7420978253493403698

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com