diff --git a/docs/high-performance/message-queue/message-queue.md b/docs/high-performance/message-queue/message-queue.md index 58b7f853..fb7d7d89 100644 --- a/docs/high-performance/message-queue/message-queue.md +++ b/docs/high-performance/message-queue/message-queue.md @@ -19,7 +19,7 @@ tag: 我们可以把消息队列看作是一个存放消息的容器,当我们需要使用消息的时候,直接从容器中取出消息供自己使用即可。由于队列 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) 参与消息传递的双方称为 **生产者** 和 **消费者** ,生产者负责发送消息,消费者负责处理消息。 diff --git a/docs/interview-preparation/resume-guide.md b/docs/interview-preparation/resume-guide.md index b4c19adf..a450d9c0 100644 --- a/docs/interview-preparation/resume-guide.md +++ b/docs/interview-preparation/resume-guide.md @@ -142,7 +142,7 @@ icon: jianli 项目介绍尽量压缩在两行之内,不需要介绍太多,但也不要随便几个字就介绍完了。 - +另外,个人收获和项目成果都是可选的,如果选择写的话,也不要花费太多篇幅,记住你的重点是介绍工作内容/个人职责。 **2、技术架构直接写技术名词就行,不要再介绍技术是干嘛的了,没意义,属于无效介绍。** diff --git a/docs/java/concurrent/completablefuture-intro.md b/docs/java/concurrent/completablefuture-intro.md index 0cc41971..0858a204 100644 --- a/docs/java/concurrent/completablefuture-intro.md +++ b/docs/java/concurrent/completablefuture-intro.md @@ -700,7 +700,7 @@ CompletableFuture.runAsync(() -> { ### 合理组合多个异步任务 -正确使用 `thenCompose()` 、 `thenCombine()` 、``acceptEither()`、`allOf()`、`anyOf() `等方法来组合多个异步任务,以满足实际业务的需求,提高程序执行效率。 +正确使用 `thenCompose()` 、 `thenCombine()` 、`acceptEither()`、`allOf()`、`anyOf() `等方法来组合多个异步任务,以满足实际业务的需求,提高程序执行效率。 实际使用中,我们还可以利用或者参考现成的异步任务编排框架,比如京东的 [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` 在实际项目中的运用。参考这篇文章,可以对项目中类似的场景进行优化,也算是一个小亮点了。这种性能优化方式比较简单且效果还不错! - [读 RocketMQ 源码,学习并发编程三大神器 - 勇哥 java 实战分享](https://mp.weixin.qq.com/s/32Ak-WFLynQfpn0Cg0N-0A):这篇文章介绍了 RocketMQ 对`CompletableFuture`的应用。具体来说,从 RocketMQ 4.7 开始,RocketMQ 引入了 `CompletableFuture`来实现异步消息处理 。 diff --git a/docs/java/concurrent/java-concurrent-questions-03.md b/docs/java/concurrent/java-concurrent-questions-03.md index dbfaf261..333c94b9 100644 --- a/docs/java/concurrent/java-concurrent-questions-03.md +++ b/docs/java/concurrent/java-concurrent-questions-03.md @@ -484,6 +484,35 @@ CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内 - **[Hippo4j](https://github.com/opengoofy/hippo4j)**:异步线程池框架,支持线程池动态变更&监控&报警,无需修改代码轻松引入。支持多种使用模式,轻松引入,致力于提高系统运行保障能力。 - **[Dynamic TP](https://github.com/dromara/dynamic-tp)**:轻量级动态线程池,内置监控告警功能,集成三方中间件线程池管理,基于主流配置中心(已支持 Nacos、Apollo,Zookeeper、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 类有什么用? diff --git a/docs/system-design/framework/spring/ioc-and-aop.md b/docs/system-design/framework/spring/ioc-and-aop.md index ee22ce08..563c1ea2 100644 --- a/docs/system-design/framework/spring/ioc-and-aop.md +++ b/docs/system-design/framework/spring/ioc-and-aop.md @@ -100,7 +100,7 @@ AOP 之所以叫面向切面编程,是因为它的核心思想就是将横切 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)