java线程池

尽意
2024-09-16 / 0 评论 / 18 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年09月16日,已超过128天没有更新,若内容或图片失效,请留言反馈。
Java线程池是在处理并发编程时非常关键的一部分,主要用于管理被多个任务共享的一组固定数量的线程资源。

线程池的关键参数详解

  1. 核心线程数(corePoolSize)

    • 这是线程池初始化时的线程数量,即使这些线程处于空闲状态,也不会被回收。这个参数对系统性能有重要影响,设置过小可能导致处理任务时延迟增加,设置过大可能会浪费系统资源。
  2. 最大线程数(maximumPoolSize)

    • 线程池允许同时运行的最大线程数量。当工作队列满了且当前运行的线程数小于最大线程数时,线程池会创建新的线程来处理任务。
  3. 存活时间(keepAliveTime)和时间单位(unit)

    • 当线程数量超过核心线程数时,这是超出数量的空闲线程在被终止前可以保持空闲的最长时间。单位是一个枚举,表示时间单位,如TimeUnit.SECONDS
  4. 工作队列(workQueue)

    • 用于存放等待执行的任务的阻塞队列。常见的选项包括直接交付队列SynchronousQueue、无界队列LinkedBlockingQueue和有界队列ArrayBlockingQueue
  5. 线程工厂(ThreadFactory)

    • 创建新线程的工厂。可以自定义线程工厂来设置线程的名字、优先级等属性。
  6. 拒绝策略(RejectedExecutionHandler)

    • 当队列满了并且线程数达到最大值时,新提交的任务如何处理。常见的策略有ThreadPoolExecutor.AbortPolicy(抛出异常)、ThreadPoolExecutor.DiscardPolicy(丢弃任务,不抛出异常)、ThreadPoolExecutor.DiscardOldestPolicy(丢弃队列最前面的任务),和ThreadPoolExecutor.CallerRunsPolicy(调用者所在的线程来执行任务)。

示例:使用ThreadPoolExecutor自定义线程池

以下是如何直接使用ThreadPoolExecutor来创建一个自定义的线程池的示例:

import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        int corePoolSize = 2;
        int maximumPoolSize = 4;
        long keepAliveTime = 10;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

        ExecutorService threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
                                                            keepAliveTime, unit, workQueue,
                                                            threadFactory, handler);

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

        threadPool.shutdown();
    }
}

Executors类常用工厂方法

  1. newFixedThreadPool(int nThreads)

    • 创建一个固定线程数的线程池。所有提交的任务都使用这些线程处理。适用于任务量预知,需要控制并发数量的场景。

    示例代码:

    ExecutorService executor = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 10; i++) {
        int taskNo = i;
        executor.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " is executing task " + taskNo);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
    executor.shutdown();
  2. newSingleThreadExecutor()

    • 创建一个单线程的执行器,这可以保证所有任务按照提交顺序串行执行。适用于需要单线程执行任务,保证顺序性和一致性的场景。

    示例代码:

    ExecutorService executor = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 5; i++) {
        int taskNo = i;
        executor.execute(() -> {
            System.out.println("Executing task " + taskNo);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
    executor.shutdown();
  3. newCachedThreadPool()

    • 创建一个可缓存线程的线程池,如果线程池的大小超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。适用于任务数动态变化的场景。

    示例代码:

    ExecutorService executor = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; i++) {
        int taskNo = i;
        executor.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " is executing task " + taskNo);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
    executor.shutdown();
  4. newScheduledThreadPool(int corePoolSize)

    • 创建一个固定数量的线程来执行定时或周期性任务。适用于需要多个后台线程执行周期任务,同时为了资源的有效利用需要限制线程数量的场景。

    示例代码:

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    Runnable beeper = () -> System.out.println("beep");
    // 安排任务每10秒钟执行一次
    ScheduledFuture<?> beeperHandle = scheduler.scheduleAtFixedRate(beeper, 0, 10, TimeUnit.SECONDS);
    // 安排在60秒后取消任务
    scheduler.schedule(() -> beeperHandle.cancel(true), 60, TimeUnit.SECONDS);

关键方法

  • execute(Runnable command): 提交一个Runnable任务用于执行。
  • submit(Callable task): 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的Future。
  • shutdown(): 启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
  • shutdownNow(): 尝试停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

状态管理

线程池的状态变化是通过内部的runStateworkerCount进行管理的,不同状态下线程池的行为也不同,比如在SHUTDOWN状态下,线程池不接受新任务,但会继续处理队列中的旧任务。

使用建议和最佳实践

  • 资源管理:对于newCachedThreadPoolnewScheduledThreadPool等可能创建大量线程的工厂方法,需要注意系统资源的使用,避免创建过多线程导致资源耗尽。
  • 任务类型和线程池策略匹配:选择最适合任务特性的线程池类型,如IO密集型、CPU密集型或混合型任务。
  • 优雅关闭:在应用程序结束时,应调用shutdown()shutdownNow()方法来优雅地关闭线程池,释放资源。

通过合理选择和使用Executors类提供的工厂方法,可以有效地管理线程资源,提高程序的并发性能和稳定性。

1

评论 (0)

取消