1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

[docs update&fix]添加并发面试问题:如何设计一个能够根据任务的优先级来执行的线程池?& 修复一些笔误

This commit is contained in:
Guide 2023-09-25 15:51:13 +08:00
parent 42b4eb6726
commit b2ae0fde18
5 changed files with 34 additions and 5 deletions

View File

@ -19,7 +19,7 @@ tag:
我们可以把消息队列看作是一个存放消息的容器,当我们需要使用消息的时候,直接从容器中取出消息供自己使用即可。由于队列 Queue 是一种先进先出的数据结构,所以消费消息时也是按照顺序来消费的。 我们可以把消息队列看作是一个存放消息的容器,当我们需要使用消息的时候,直接从容器中取出消息供自己使用即可。由于队列 Queue 是一种先进先出的数据结构,所以消费消息时也是按照顺序来消费的。
![Message queue](https://oss.javaguide.cn/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/message-queue-small.png) ![](https://oss.javaguide.cn/github/javaguide/high-performance/message-queue/message-queue-small.png)
参与消息传递的双方称为 **生产者****消费者** ,生产者负责发送消息,消费者负责处理消息。 参与消息传递的双方称为 **生产者****消费者** ,生产者负责发送消息,消费者负责处理消息。

View File

@ -142,7 +142,7 @@ icon: jianli
项目介绍尽量压缩在两行之内,不需要介绍太多,但也不要随便几个字就介绍完了。 项目介绍尽量压缩在两行之内,不需要介绍太多,但也不要随便几个字就介绍完了。
另外,个人收获和项目成果都是可选的,如果选择写的话,也不要花费太多篇幅,记住你的重点是介绍工作内容/个人职责。
**2、技术架构直接写技术名词就行不要再介绍技术是干嘛的了没意义属于无效介绍。** **2、技术架构直接写技术名词就行不要再介绍技术是干嘛的了没意义属于无效介绍。**

View File

@ -700,7 +700,7 @@ CompletableFuture.runAsync(() -> {
### 合理组合多个异步任务 ### 合理组合多个异步任务
正确使用 `thenCompose()``thenCombine()```acceptEither()``allOf()``anyOf() `等方法来组合多个异步任务,以满足实际业务的需求,提高程序执行效率。 正确使用 `thenCompose()``thenCombine()``acceptEither()``allOf()``anyOf() `等方法来组合多个异步任务,以满足实际业务的需求,提高程序执行效率。
实际使用中,我们还可以利用或者参考现成的异步任务编排框架,比如京东的 [asyncTool](https://gitee.com/jd-platform-opensource/asyncTool) 。 实际使用中,我们还可以利用或者参考现成的异步任务编排框架,比如京东的 [asyncTool](https://gitee.com/jd-platform-opensource/asyncTool) 。
@ -708,7 +708,7 @@ CompletableFuture.runAsync(() -> {
## 后记 ## 后记
这篇文章只是简单介绍了 `CompletableFuture` 比较常用的一些 API 。如果想要深入学习的话,还可以多找一些书籍和博客看,比如下面几篇文章就挺不错: 这篇文章只是简单介绍了 `CompletableFuture` 的核心概念和比较常用的一些 API 。如果想要深入学习的话,还可以多找一些书籍和博客看,比如下面几篇文章就挺不错:
- [CompletableFuture 原理与实践-外卖商家端 API 的异步化 - 美团技术团队](https://tech.meituan.com/2022/05/12/principles-and-practices-of-completablefuture.html):这篇文章详细介绍了 `CompletableFuture` 在实际项目中的运用。参考这篇文章,可以对项目中类似的场景进行优化,也算是一个小亮点了。这种性能优化方式比较简单且效果还不错! - [CompletableFuture 原理与实践-外卖商家端 API 的异步化 - 美团技术团队](https://tech.meituan.com/2022/05/12/principles-and-practices-of-completablefuture.html):这篇文章详细介绍了 `CompletableFuture` 在实际项目中的运用。参考这篇文章,可以对项目中类似的场景进行优化,也算是一个小亮点了。这种性能优化方式比较简单且效果还不错!
- [读 RocketMQ 源码,学习并发编程三大神器 - 勇哥 java 实战分享](https://mp.weixin.qq.com/s/32Ak-WFLynQfpn0Cg0N-0A):这篇文章介绍了 RocketMQ 对`CompletableFuture`的应用。具体来说,从 RocketMQ 4.7 开始RocketMQ 引入了 `CompletableFuture`来实现异步消息处理 。 - [读 RocketMQ 源码,学习并发编程三大神器 - 勇哥 java 实战分享](https://mp.weixin.qq.com/s/32Ak-WFLynQfpn0Cg0N-0A):这篇文章介绍了 RocketMQ 对`CompletableFuture`的应用。具体来说,从 RocketMQ 4.7 开始RocketMQ 引入了 `CompletableFuture`来实现异步消息处理 。

View File

@ -484,6 +484,35 @@ CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内
- **[Hippo4j](https://github.com/opengoofy/hippo4j)**:异步线程池框架,支持线程池动态变更&监控&报警,无需修改代码轻松引入。支持多种使用模式,轻松引入,致力于提高系统运行保障能力。 - **[Hippo4j](https://github.com/opengoofy/hippo4j)**:异步线程池框架,支持线程池动态变更&监控&报警,无需修改代码轻松引入。支持多种使用模式,轻松引入,致力于提高系统运行保障能力。
- **[Dynamic TP](https://github.com/dromara/dynamic-tp)**:轻量级动态线程池,内置监控告警功能,集成三方中间件线程池管理,基于主流配置中心(已支持 Nacos、ApolloZookeeper、Consul、Etcd可通过 SPI 自定义实现)。 - **[Dynamic TP](https://github.com/dromara/dynamic-tp)**:轻量级动态线程池,内置监控告警功能,集成三方中间件线程池管理,基于主流配置中心(已支持 Nacos、ApolloZookeeper、Consul、Etcd可通过 SPI 自定义实现)。
### 如何设计一个能够根据任务的优先级来执行的线程池?
这是一个常见的面试问题,本质其实还是在考察求职者对于线程池以及阻塞队列的掌握。
我们上面也提到了,不同的线程池会选用不同的阻塞队列作为任务队列,比如`FixedThreadPool` 使用的是`LinkedBlockingQueue`(无界队列),由于队列永远不会被放满,因此`FixedThreadPool`最多只能创建核心线程数的线程。
假如我们需要实现一个优先级任务线程池的话,那可以考虑使用 `PriorityBlockingQueue` (优先级阻塞队列)作为任务队列(`ThreadPoolExecutor` 的构造函数有一个 `workQueue` 参数可以传入任务队列)。
![ThreadPoolExecutor构造函数](https://oss.javaguide.cn/github/javaguide/java/concurrent/common-parameters-of-threadpool-workqueue.jpg)
`PriorityBlockingQueue` 是一个支持优先级的无界阻塞队列,可以看作是线程安全的`PriorityQueue`,两者底层都是使用小顶堆形式的二叉堆,即值最小的元素优先出队。不过,`PriorityQueue` 不支持阻塞操作并且是有界的。
要想让 `PriorityBlockingQueue` 实现对任务的排序,传入其中的任务必须是具备排序能力的,方式有两种:
1. 提交到线程池的任务实现 `Comparable` 接口,并重写 `compareTo` 方法来指定任务之间的优先级比较规则。
2. 创建 `PriorityBlockingQueue` 时传入一个 `Comparator` 对象来指定任务之间的排序规则(推荐)。
不过,这存在一些风险和问题,比如:
- `PriorityBlockingQueue` 是无界的,可能堆积大量的请求,从而导致 OOM。
- 可能会导致饥饿问题,即低优先级的任务长时间得不到执行。
- 由于需要对队列中的元素进行排序操作,因此会降低性能。
对于 OOM 这个问题的解决比较简单粗暴,就是继承`PriorityBlockingQueue` 并重写一下 `offer` 方法(入队)的逻辑,当插入的元素数量超过指定值就返回 false 。
饥饿问题这个可以通过优化设计来解决(比较麻烦),比如等待时间过长的任务会被移除并重新添加到队列中,但是优先级会被提升。
对于性能方面的影响,是没办法避免的,毕竟需要对任务进行排序操作。并且,对于大部分业务场景来说,这点性能影响是可以接受的。
## Future ## Future
### Future 类有什么用? ### Future 类有什么用?

View File

@ -100,7 +100,7 @@ AOP 之所以叫面向切面编程,是因为它的核心思想就是将横切
OOP 不能很好地处理一些分散在多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流、接口幂等等),这些行为通常被称为 **横切关注点cross-cutting concerns** 。如果我们在每个类或对象中都重复实现这些行为,那么会导致代码的冗余、复杂和难以维护。 OOP 不能很好地处理一些分散在多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流、接口幂等等),这些行为通常被称为 **横切关注点cross-cutting concerns** 。如果我们在每个类或对象中都重复实现这些行为,那么会导致代码的冗余、复杂和难以维护。
AOP 可以将横切关注点(如日志记录、事务管理、权限控制、接口限流、接口幂等等)从**核心业务逻辑core concerns核心关注点**中分离出来,实现关注点的分离。 AOP 可以将横切关注点(如日志记录、事务管理、权限控制、接口限流、接口幂等等)从 **核心业务逻辑core concerns核心关注点** 中分离出来,实现关注点的分离。
![](https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/crosscut-logic-and-businesslogic-separation%20%20%20%20%20%20.png) ![](https://oss.javaguide.cn/github/javaguide/system-design/framework/spring/crosscut-logic-and-businesslogic-separation%20%20%20%20%20%20.png)