面试题 - parallelStream() 有什么缺点 - ForkJoinPool,它和传统的线程池(如 ThreadPoolExecutor)的区别

  1. 底层使用 ForkJoinPool ,不同与线程池
  2. 适用于连续的内存分布的数据结构,如数组和ArrayList(),并不适用于链表
  3. 适用于 cpu 密集的工作,cpu 的核数多效率高,并行流能否真正提高性能,很大程度上取决于系统的可用CPU 核心数。;不适用于 IO密集型
  4. 线程安全有调试困难程度

ForkJoinPool,它和传统的线程池(如 ThreadPoolExecutor)的区别

  1. ForkJoinPool 适用于分治任务,即适用于任务被分成小块执行,执行完后将结果合并,如并行处理大型数据集、并行递归算法等。使用“工作窃取算法(work-stealing algorithm)”。这种算法允许空闲的线程从其他繁忙线程的队列中“窃取”任务。
  2. 线程池:适用于一般的并发场景,比如处理独立的任务队列、异步任务执行、IO密集型操作等。这些任务往往是互相独立的,并不需要分解和合并。任务通常是独立的、一次性提交
  3. ForkJoinPool.commonPool()parallelStream() 默认使用的是全局的公共 ForkJoinPool,所有调用 parallelStream() 的线程默认会共享这个线程池。也叫做 ForkJoinPool.commonPool()。这个池的大小通常等于可用的 CPU 核心数。
  4. 线程池:线程池(如 ThreadPoolExecutor)通常需要你自己定义和配置,比如核心线程数、最大线程数、队列容量等,且每个线程池是独立的,可以根据场景灵活调整
### ForkJoinPool与自定义线程池的性能对比及适用场景 #### 一、ForkJoinPool的特点及其优势 ForkJoinPool是一种专门设计用于处理大量细粒度任务的线程池,其核心思想是通过分治法(Divide and Conquer)来分解大任务为多个子任务并行执行。这种机制特别适合于可以被拆分为更小子任务的工作负载。 - **工作窃取算法**:ForkJoinPool采用了工作窃取(Work Stealing)算法,在某些线程完成自己的任务后会主动寻找其他线程的任务队列中的任务进行处理[^1]。这种方式能够有效减少线程空闲时间,提高CPU利用率。 - **高效的任务调度**:由于ForkJoinPool内部维护了一个双端队列(Deque),每个线程都有独立的任务队列,因此减少了锁竞争的概率,从而提升了整体性能[^2]。 - **适用于计算密集型任务**:对于那些可以通过递归方式分割成较小部分的大规模数据集上的复杂运算来说,比如矩阵乘法、快速傅里叶变换等,ForkJoinPool表现尤为出色[^3]。 #### 二、自定义线程池的优势 尽管ForkJoinPool具有上述优点,但在实际开发过程中也存在局限性,而传统的自定义线程池则提供了更大的灵活性: - **定制化程度高**:开发者可以根据业务需求精确控制线程数量、排队策略以及其他行为特性。例如,`ThreadPoolExecutor`允许指定拒绝策略(Rejected Execution Handler)、保持活动时间最大池大小等参数。 - **支持多种类型的任务**:除了简单的Runnable接口外,还可以轻松集成Callable/Future模式实现异步调用返回结果的功能;同时也能很好地兼容定时任务的需求。 - **更好的调试体验**:相比于高度优化但相对复杂的ForkJoinPool结构而言,普通的Executors工厂创建出来的固定大小或者缓存型线程池往往更容易理解追踪潜在问题所在之处[^4]。 #### 三、两者之间的主要差异总结如下表所示 | 特性 | ForkJoinPool | 自定义线程池 | |---------------------|---------------------------------------|----------------------------------| | 主要用途 | 计算密集型任务 | IO 密集型或其他通用任务 | | 调度机制 | 工作窃取 | FIFO/LIFO 或者优先级队列 | | 并发模型 | 基于分治的思想 | 单纯的任务分配 | | 配置选项 | 较少 | 更加灵活 | | 易用性学习曲线 | 存在一定的门槛 | 相对简单直观 | #### 四、典型应用场景举例说明 - 如果项目涉及大量的数值计算或者是图像渲染等领域内的批量处理,则推荐采用ForkJoinPool因为它的架构非常适合此类场合下的资源最大化利用^。 - 对于网络爬虫抓取网页内容这样的I/O绑定操作或是数据库查询之类的长时间等待事件驱动流程下更适合选用常规意义上的可调整配置版图式化的标准库组件即传统意义上面提到过的那种形式化表达出来的东西. ```java // 创建一个带有两个核心线程的标准线程池实例演示代码片段 import java.util.concurrent.*; public class ThreadPoolExample { public static void main(String[] args){ ExecutorService executor = new ThreadPoolExecutor( 2, // core pool size 5, // maximum pool size 10L, TimeUnit.SECONDS, new LinkedBlockingQueue<>() ); Runnable task = () -> System.out.println("Executing Task"); for(int i=0;i<8;i++) { executor.submit(task); } ((ThreadPoolExecutor)executor).shutdown(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值