当前位置: 首页 > news >正文

线程池的工作原理

  • 固定线程池:线程池中的线程数是固定的,线程池创建时就已经设定了固定的线程数量。在任务提交时,线程池会将任务分配给空闲的线程执行。如果所有线程都在执行任务,新的任务会被放到任务队列中,直到有线程空闲出来。

  • 线程复用:线程池中的线程会被复用,也就是说,线程池中的线程在任务执行完后并不会销毁,而是会被复用来执行其他任务。这能够提高任务的处理效率,避免了频繁创建和销毁线程的开销。

  • 队列管理:如果提交的任务超过了线程池中线程的数量,任务会被放入任务队列中。队列有大小限制,超出限制时会根据拒绝策略来处理(例如抛出异常、丢弃任务等)。

代码示例

import java.util.concurrent.*;

public class FixedThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(13); // 创建一个固定大小为 13 的线程池

        // 提交多个任务
        for (int i = 0; i < 20; i++) {
            int taskId = i;
            executorService.execute(() -> {
                System.out.println("任务 " + taskId + " 开始执行, 线程: " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // 模拟任务执行过程
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                System.out.println("任务 " + taskId + " 执行完成");
            });
        }

        executorService.shutdown(); // 关闭线程池
    }
}

代码解释:

  1. 创建线程池Executors.newFixedThreadPool(13) 创建一个固定大小为 13 的线程池。线程池可以同时执行 13 个任务。

  2. 提交任务:通过 executorService.execute() 方法提交 20 个任务,线程池会把这些任务分配给空闲的线程来执行。如果有超过 13 个任务,其他任务会被放入任务队列中,等待有线程空闲时再执行。

  3. 关闭线程池executorService.shutdown() 会关闭线程池,不再接受新的任务。所有已经提交的任务会继续执行,直到执行完毕。

注意事项:

  • 线程池大小:根据任务的数量和执行的需求,线程池大小应该合理设置。创建过多的线程会导致资源竞争和上下文切换的开销;线程池太小则可能导致任务堆积和延迟执行。

  • 关闭线程池:通过 shutdown()shutdownNow() 来关闭线程池,建议在所有任务完成后关闭线程池。如果线程池不再使用,及时关闭能够释放资源。

  • 任务队列:线程池会使用一个任务队列来缓存那些等待执行的任务。如果任务队列满了,你可能会遇到任务拒绝的情况。你可以通过设置自定义的拒绝策略来应对这种情况。

扩展内容

定时任务池

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

这行代码的作用是创建一个大小为 1 的定时任务线程池。ScheduledExecutorServiceExecutorService 的一个子接口,它提供了调度任务的功能,包括定时任务和周期性任务的执行。

关键点:

  1. 定时任务ScheduledExecutorService 允许你以固定的延迟时间执行任务,或者按照固定的时间间隔周期性地执行任务。

  2. 线程池大小:在这个例子中,线程池的大小为 1,意味着线程池中只有一个线程可用于执行任务。如果你提交多个定时任务,它们会依次排队执行。只有一个线程的线程池适用于那些对并发要求不高的任务。

  3. 常见方法

    • schedule(): 用于在指定延迟后执行一个任务。

    • scheduleAtFixedRate(): 用于按固定频率执行任务,即每隔一定时间执行一次,适用于周期性任务。

    • scheduleWithFixedDelay(): 用于在执行完一个任务后延迟一定时间再执行下一个任务,适用于任务间有延迟需求的情况。

代码示例:

import java.util.concurrent.*;

public class ScheduledExecutorServiceExample {
    public static void main(String[] args) {
        // 创建一个定时任务线程池,池中只有一个线程
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

        // 使用 schedule() 方法定时执行任务
        scheduledExecutorService.schedule(() -> {
            System.out.println("任务执行了,延迟 2 秒");
        }, 2, TimeUnit.SECONDS);  // 延迟 2 秒执行任务

        // 使用 scheduleAtFixedRate() 方法按固定频率执行任务
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            System.out.println("周期性任务执行,每 3 秒执行一次");
        }, 0, 3, TimeUnit.SECONDS);  // 初始延迟 0 秒,之后每 3 秒执行一次

        // 使用 scheduleWithFixedDelay() 方法按固定延迟执行任务
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            System.out.println("任务执行完后,延迟 1 秒再执行下一个任务");
        }, 0, 1, TimeUnit.SECONDS);  // 初始延迟 0 秒,任务之间的间隔 1 秒

        // 模拟主线程等待一段时间后关闭线程池
        try {
            Thread.sleep(10000);  // 等待 10 秒,观察任务执行情况
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        scheduledExecutorService.shutdown();  // 关闭线程池
    }
}

代码解释:

  1. schedule() 方法:用于指定一个任务在指定延迟后执行。这里我们设置了延迟 2 秒后执行任务。

  2. scheduleAtFixedRate() 方法:用于周期性地执行任务,初始延迟为 0 秒,每 3 秒执行一次。

  3. scheduleWithFixedDelay() 方法:用于按固定的延迟时间执行任务,任务执行完成后会延迟 1 秒再执行下一个任务。

  4. shutdown():在所有任务完成后,调用 shutdown() 方法来关闭线程池,避免资源泄露。

注意事项:

  • 线程池的大小:由于线程池的大小为 1,所有提交的任务将依次排队执行。如果有多个周期性任务,它们将按顺序执行,无法并行。如果需要并行执行多个任务,可以增加线程池的大小。

  • 任务提交方式:你可以根据需要选择使用 schedule(), scheduleAtFixedRate(), 或 scheduleWithFixedDelay() 方法。选择合适的方式取决于任务的特性,比如是否是周期性任务,是否需要固定的时间间隔等。

  • 关闭线程池:使用 shutdown()shutdownNow() 来关闭线程池。要确保所有任务执行完之后再关闭线程池,避免任务被中断。

执行任务方式

executorService.execute(() -> { ... }) 是 Java 中使用线程池执行任务的一种方式,它是 ExecutorService 接口中的方法之一。这个方法接受一个实现了 Runnable 接口的任务,并将其提交给线程池执行。

在这个例子中,execute() 方法会提交一个无返回值的任务(即 Runnable)给线程池执行。当你使用 Lambda 表达式时,() -> { ... } 是一种简洁的写法,表示创建一个匿名 Runnable 实现类。

ExecutorService executorService = Executors.newFixedThreadPool(2); // 创建一个包含两个线程的线程池

executorService.execute(() -> {
    System.out.println("任务开始执行");
    // 任务逻辑
    try {
        Thread.sleep(1000); // 模拟任务执行过程中的等待
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    System.out.println("任务执行完成");
});

executorService.shutdown(); // 关闭线程池

关键点:

  • execute() 方法不会返回值,适用于那些没有返回结果的任务。如果你需要获取任务的执行结果,应该使用 submit() 方法,它返回一个 Future 对象。

  • Runnable 接口代表一个无返回值的任务,你可以通过 Lambda 表达式或者匿名内部类的方式来实现。

  • ExecutorServiceExecutor 接口的子接口,提供了更多的控制方法,如 shutdown() 用来关闭线程池。

注意事项:

  1. 如果线程池已经被关闭,或者正在终止,调用 execute() 提交任务时会抛出 RejectedExecutionException

eg:

  1. 如果你希望在任务执行后获取结果,使用 submit() 而不是 execute(),因为 submit() 返回一个 Future 对象,允许你获取执行结果或者捕获异常。

http://www.dtcms.com/a/113557.html

相关文章:

  • 线代[12]|《高等几何》陈绍菱(1984.9)(文末有对三大空间的分析及一个合格数学系毕业生的要求)
  • Python 语法学习 1(类比 java 学习)-附Python 中 self
  • 前端用用jsonp的方式解决跨域问题
  • [ICLR 2025]Biologically Plausible Brain Graph Transformer
  • Reids 的io并发模型
  • 程序化广告行业(60/89):算法优化与DSP系统实例解析
  • Linux系统程序设计:从入门到高级Day03
  • 第八章:流量治理_《凤凰架构:构建可靠的大型分布式系统》
  • DDPM 做了什么
  • 2007-2019年各省地方财政其他支出数据
  • 格式工厂怎样插入内置音频文件
  • 硬件工程师面试问题(五):蓝牙面试问题与详解
  • 在响应式网页的开发中使用固定布局、流式布局、弹性布局哪种更好
  • vllm作为服务启动,无需额外编写sh文件,一步到位【Ubuntu】
  • 『Linux_网络』 第一章 网络基础概念
  • 分表字段选择策略:以电商交易订单为例的最佳实践
  • Java项目之基于ssm的怀旧唱片售卖系统(源码+文档)
  • 大数据时代的隐私保护:区块链技术的创新应用
  • 通过构造函数和几何条件,研究了不同函数的最近点存在性、性质及单调性
  • ZKmall开源商城多云高可用架构方案:AWS/Azure/阿里云全栈实践
  • 紧急更新!MinIO发布RELEASE.2025-04-03T14-56-28Z版本,修复高危漏洞CVE-2025-31489,用户需立即升级!
  • raft协议中一条数据写入流程
  • Java 实现插入排序:[通俗易懂的排序算法系列之三]
  • 文献总结:TPAMI综述BEV感知—Delving into the devils of bird‘s-eye-view perception
  • Socket编程TCP
  • HarmonyOS:WebView 控制及 H5 原生交互实现
  • 硬件学习之器件篇-蜂鸣器
  • 第三章 react redux的学习之redux和react-redux,@reduxjs/toolkit依赖结合使用
  • use_tempaddr 笔记250405
  • setj集合