• 使用 ThreadPoolExecutor 管理线程池


    使用 ThreadPoolExecutor 管理线程池

    多线程编程中,线程池是一个关键的工具,可以有效地管理线程的生命周期,提高程序的性能和资源利用率。Java提供了一个强大的线程池实现,称为 ThreadPoolExecutor。

    1. 引入ThreadPoolExecutor

    ExecutorService executor = new ThreadPoolExecutor(
        corePoolSize,          // 核心线程数
        maximumPoolSize,       // 最大线程数
        keepAliveTime,         // 线程空闲时间
        timeUnit,              // 时间单位
        workQueue,             // 工作队列
        threadFactory,         // 线程工厂
        rejectionPolicy        // 拒绝策略
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2. 参数解释

    a. corePoolSize (核心线程数)

    • 核心线程数定义了线程池中保持活动状态的最少线程数量。
    • 当提交一个任务时,线程池会创建一个线程来处理任务,即使此时线程池中已经存在空闲的线程。
    • 如果线程池中的线程数小于 corePoolSize,即使其他线程都是空闲的,ThreadPoolExecutor 也会优先创建新的线程来处理任务。

    b. maximumPoolSize (最大线程数)

    • 最大线程数定义了线程池中允许的最大线程数量。
    • 如果工作队列已满且活动线程数小于 maximumPoolSize,线程池会创建新线程来处理任务。

    c. keepAliveTime (线程空闲时间)

    • 当线程数超过 corePoolSize,空闲线程的最大存活时间。
    • 超过这个时间,空闲线程会被终止,直到线程数量回到 corePoolSize。

    d. timeUnit (时间单位)

    • 用于指定 keepAliveTime 的单位,可以是秒、毫秒等。

    e. workQueue (工作队列)

    • 任务的排队队列,用于暂时存放等待执行的任务。
    • 可以是 LinkedBlockingQueue(无界队列)或 ArrayBlockingQueue(有界队列)等。
    • 以下是一些常用的工作队列:
    1. LinkedBlockingQueue(链表阻塞队列)

      • LinkedBlockingQueue 是一个基于链表实现的无界队列,可以无限制地增加元素。
      • 它的容量理论上可以是无限大的,适用于任务量比较大的场景。
      BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
      
      • 1
    2. ArrayBlockingQueue(数组阻塞队列)

      • ArrayBlockingQueue 是一个基于数组实现的有界队列,必须指定队列的容量。
      • 当队列已满时,会拒绝新的任务。
      BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(capacity);
      
      • 1
    3. SynchronousQueue(同步队列)

      • SynchronousQueue 是一个没有存储元素的队列,每个插入操作必须等待另一个线程的对应移除操作。
      • 适用于直接将任务交给线程执行,而不需要缓冲任务的场景。
      BlockingQueue<Runnable> workQueue = new SynchronousQueue<>();
      
      • 1
    4. PriorityBlockingQueue(优先级队列)

      • PriorityBlockingQueue 是一个无界的优先级队列,可以根据元素的优先级顺序进行处理。
      • 需要任务实现 Comparable 接口或者传入自定义的 Comparator
      BlockingQueue<Runnable> workQueue = new PriorityBlockingQueue<>();
      
      • 1
    5. DelayQueue(延迟队列)

      • DelayQueue 是一个无界的队列,用于存放实现了 Delayed 接口的元素。
      • 元素只有在其指定的延迟时间到了才能从队列中取出。
      BlockingQueue<Runnable> workQueue = new DelayQueue<>();
      
      • 1
    6. LinkedTransferQueue(链表传输队列)

      • LinkedTransferQueue 是一个无界队列,可以在生产者和消费者之间传输元素。
      • 可以通过 tryTransfer 方法尝试直接将元素传输给消费者,如果不成功则添加到队列中。
      BlockingQueue<Runnable> workQueue = new LinkedTransferQueue<>();
      
      • 1
    7. LinkedBlockingDeque(链表双向阻塞队列)

      • LinkedBlockingDeque 是一个基于链表实现的无界双向队列,可以在队头和队尾插入、移除元素。
      BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>();
      
      • 1
    8. 自定义工作队列

      • 你也可以实现自己的工作队列,只需要实现 BlockingQueue 接口。

    总的来说,选择工作队列的类型取决于你的具体需求和场景。不同的队列类型在不同的场景下有着不同的优劣势,需要根据实际情况来选择。

    f. threadFactory (线程工厂)

    • 用于创建新线程的工厂,可以自定义线程的名字、优先级等。

    g. rejectionPolicy (拒绝策略)

    • 当工作队列和线程池都满了,如何处理新提交的任务。有几种策略可选:
      • ThreadPoolExecutor.AbortPolicy:直接抛出异常。
      • ThreadPoolExecutor.CallerRunsPolicy:由调用者所在的线程执行任务。
      • ThreadPoolExecutor.DiscardPolicy:直接丢弃任务。
      • ThreadPoolExecutor.DiscardOldestPolicy:丢弃最老的任务。

    3. 使用示例

    import java.util.concurrent.*;
    
    public class ThreadPoolExample {
        public static void main(String[] args) {
            int corePoolSize = 5;
            int maximumPoolSize = 10;
            long keepAliveTime = 60L;
            TimeUnit timeUnit = TimeUnit.SECONDS;
            BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);
            ThreadFactory threadFactory = Executors.defaultThreadFactory();
            RejectedExecutionHandler rejectionPolicy = new ThreadPoolExecutor.CallerRunsPolicy();
    
            ExecutorService executor = new ThreadPoolExecutor(
                    corePoolSize,
                    maximumPoolSize,
                    keepAliveTime,
                    timeUnit,
                    workQueue,
                    threadFactory,
                    rejectionPolicy
            );
    
            // 提交任务给线程池
            for (int i = 0; i < 20; i++) {
                executor.execute(new Task(i));
            }
    
            // 关闭线程池
            executor.shutdown();
        }
    
        static class Task implements Runnable {
            private final int taskId;
    
            public Task(int taskId) {
                this.taskId = taskId;
            }
    
            @Override
            public void run() {
                System.out.println("Task " + taskId + " is running.");
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这个示例中,我们创建了一个 ThreadPoolExecutor,配置了各种参数,然后提交了20个任务给线程池执行。

    protected ExecutorService executorService= new ThreadPoolExecutor(
                10,
                15,
                30,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1024),
                r -> {
                    Thread t = new Thread(r);
                    t.setName("SAVE_D_STORAGE");
                    t.setPriority(Thread.NORM_PRIORITY + 2); // 增加优先级
                    return t;
                },
                new ThreadPoolExecutor.CallerRunsPolicy());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4. 应用场景

    ThreadPoolExecutor 适用于需要在后台执行异步任务的场景,比如:

    • Web 服务器处理请求时,可以使用线程池来处理每个请求,提高并发处理能力。
    • 后台任务的批量处理,比如数据同步、清理等。
    • 提升程序性能,避免频繁创建和销毁线程的开销。

    结语

    ThreadPoolExecutor 是 Java 多线程编程中非常重要的工具,能够高效地管理线程的生命周期,提高程序性能和资源利用率。合理配置线程池参数,选择适当的拒绝策略,是保证系统稳定性和性能的关键。

  • 相关阅读:
    备战2024秋招面试题-最左匹配原则、索引失效情况、算法(最长回文子串)
    别再吹捧什么区块链,元宇宙,Web3了,真正具有颠覆性的估计只有AI
    docker常用命令
    【STM32】STM32Cube和HAL库使用初体验
    亚马逊、速卖通、国际站测评需要什么条件?怎么养号?
    基于vue-web-terminal插件实现网页终端下载动画
    9.8 段错误,虚拟内存,内存映射 CSAPP
    基于.NetCore开发博客项目 StarBlog - (8) 分类层级结构展示
    v0.9.6 开源跨平台个人知识管理工具 TidGi-Desktop
    java生成zip压缩包文件
  • 原文地址:https://blog.csdn.net/ityqing/article/details/133748679