Java线程池解读:从入门到精通,核心参数全掌握!

createh53个月前 (02-07)技术教程36

Java中的线程池是执行异步任务的重要工具。它们允许我们有效地复用已存在的线程,避免了线程创建和销毁的额外开销。本文将深入探讨Java的线程池机制,特别是Executor框架,以及如何有效地使用它。

为什么要使用线程池?

  • 资源重用:线程创建和销毁需要时间和资源。通过线程池,我们可以重用已存在的线程。
  • 控制并发线程数:线程池允许我们限制并发线程的数量,防止资源耗尽。
  • 管理任务队列:如果所有线程都在忙碌,新任务可以在队列中等待,直到有线程可用。
  • 灵活的线程管理策略:例如,定时任务、定期任务等。

Java中的Executor框架

Java提供了Executor框架来支持线程池。其中,Executors类提供了多种静态方法来创建线程池。

import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

        for (int i = 0; i < 10; i++) {
            final int index = i;
            fixedThreadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " is executing task " + index);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        fixedThreadPool.shutdown();
    }
}

上面的代码创建了一个固定大小为5的线程池。我们提交了10个任务,因此,一开始只有5个任务会被执行。其余任务会等待其他任务完成后再执行。

ThreadPoolExecutor

ThreadPoolExecutor是线程池的核心实现。它提供了很多参数,让我们可以高度定制线程池的行为。

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5, // 核心线程数
    10, // 最大线程数
    60, // 线程空闲时间
    TimeUnit.SECONDS, // 时间单位
    new LinkedBlockingQueue<>(100), // 任务队列
    new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略
);

拒绝策略

当任务队列满,并且已达到最大线程数时,线程池如何处理新提交的任务?这就是所谓的“拒绝策略”。

Java提供了以下四种策略:

  • AbortPolicy: 抛出RejectedExecutionException异常。
  • CallerRunsPolicy: 调用者线程运行任务。
  • DiscardOldestPolicy: 丢弃队列中最旧的任务。
  • DiscardPolicy: 丢弃当前任务。

线程池参数的设置关键在于平衡系统资源的使用和任务的响应时间。以下是ThreadPoolExecutor的主要参数及其最佳实践和设置原则:

1. 核心线程数(Core Pool Size)

定义:线程池启动后默认的线程数,即使它们处于空闲状态。

最佳实践

  • 对于CPU密集型任务:核心线程数可以设置为CPU核心数 + 1。
  • 对于IO密集型任务:可以设置为CPU核心数的两倍。
  • 在多数情况下,默认为机器的核心数。

2. 最大线程数(Maximum Pool Size)

定义:线程池中允许存在的最大线程数。

最佳实践

  • 该值应大于核心线程数。
  • 根据系统负载和资源可用性来设定。
  • 不应设置过大,否则可能导致系统资源过度使用。

3. 线程空闲时间(Keep-alive Time)

定义:超过核心线程数的线程在空闲时,会等待新任务的最大时间。超过这个时间还没有新任务,它们将被终止。

最佳实践

  • 对于快速响应的系统,可以设置为较低的值。
  • 对于资源敏感的系统,应增加此时间,以减少线程的频繁创建和销毁。

4. 任务队列(Work Queue)

定义:用于存放等待执行的任务。

最佳实践

  • 直接提交队列(SynchronousQueue):直接将任务提交给线程而不保持它们。适用于处理大量短时间任务的场景。
  • 有界任务队列(ArrayBlockingQueue):具有固定大小的队列。使用此队列时,当所有核心线程都在工作,队列也满了,那么会开始创建超出核心线程数的线程,直到达到最大线程数或队列满。
  • 无界任务队列(LinkedBlockingQueue):队列的大小为Integer.MAX_VALUE。这意味着,任务永远不会被拒绝。但可能导致系统资源耗尽。
  • 优先任务队列(PriorityBlockingQueue):任务按照它们的优先级进行执行。

5. 拒绝策略(Rejected Execution Handler)

定义:当任务不能由线程池处理时(例如,队列已满),如何处理这些任务。

最佳实践

  • AbortPolicy:默认策略,抛出RejectedExecutionException。
  • CallerRunsPolicy:由调用线程处理该任务。
  • DiscardOldestPolicy:丢弃最旧的任务。
  • DiscardPolicy:不处理,直接丢弃。

原则和注意事项:

  • 资源限制:在设置线程池大小时,考虑系统和JVM的资源限制。
  • 任务的性质:CPU密集型任务和IO密集型任务对线程的需求是不同的。
  • 响应时间:如果系统需要快速响应,考虑降低线程的空闲时间。
  • 系统的监控:定期监控线程池的状态,确保它运行在最佳状态。
  • 动态调整:根据系统的实际运行情况,动态调整线程池的参数。

总的来说,线程池参数的设置是一个试错的过程,需要根据系统的实际需求和负载来进行调整。

相关文章

转行学习Java,跟着我的步骤学,保准你拿下10k以上的工作

上周一刚入职不久,是在上海的一家软件公司,税前11K,五险一金,996的工作制,已经上班了一个月,说下自己的感受。因为我专科毕业4年,之前一直在做电商运营,大专学的专业是电子商务,所以我包装了两年的工...

真正的Java学习从入门到精通,只需一个选择

正如马云所说,“很多人还没搞清楚什么是PC互联网,移动互联来了,还没搞清楚移动互联的时候,大数据时代又来了”大数据,“读心术”、“未卜先知”,用户在互联网上的一切行为都会留下数据,而通过对这些数据的分...

Java小白从零基础入门到进阶,看什么书比较好呢?

虽然视频学习资料是许多人入门或提升编程的首选,但是书籍材料对学习者来讲,也是必须要看的。尤其对于处在不同能力阶段的人来讲,选择适合自己学习的书尤为重要。那么,Java学习看什么书比较好呢?本文千锋武汉...

全新发布!从入门到精通的Spring源码笔记,让你真正读懂看懂源码

前言在现代软件开发中,Spring 框架无疑是最受欢迎和广泛使用的 Java 开发框架之一。它不仅提供了丰富的功能和灵活的配置,还极大地简化了企业级应用的开发。然而,对于许多开发者来说,Spring...

任务编排:CompletableFuture从入门到精通

前言最近遇到了一个业务场景,涉及到多数据源之间的请求的流程编排,正好看到了一篇某团介绍CompletableFuture原理和使用的技术文章,主要还是涉及使用层面。网上很多文章涉及原理的部分讲的不是特...

Alibaba内部的python入门到精通背记手册+面试宝典,GitHub已爆赞

突如其来的新冠肺炎疫情打乱了各行业发展的节奏,但 5G 的到来带动了 人工智能、大数据、物联网、云计算的快速发展,也衍生出了很多新兴产业和新 的工作岗位。 2020年全国Python软件工程师人才缺...