线程池相关知识

线程池

Java线程池详解

线程池概念与优势

线程池是一种线程使用模式,它预先创建一组线程,并通过管理这些线程来执行任务,避免了频繁创建和销毁线程的开销。主要优势包括:

  • 降低资源消耗,复用线程避免创建/销毁开销
  • 提高响应速度,任务到达时可立即执行
  • 提高线程的可管理性,统一分配、调优和监控
  • 防止系统因线程过多而导致的资源耗尽

ThreadPoolExecutor的七大核心参数

  1. corePoolSize(核心线程数)
    • 线程池保持的最小线程数
    • 即使这些线程处于空闲状态也不会被回收(除非设置了allowCoreThreadTimeOut)
    • 线程池创建后,可以预热启动所有核心线程
  1. maximumPoolSize(最大线程数)
    • 线程池允许创建的最大线程数
    • 当工作队列已满且活动线程数量达到corePoolSize后,新提交的任务会创建新线程直到达到此上限
  1. keepAliveTime(线程存活时间)
    • 当线程数超过核心线程数时,多余的空闲线程在终止前等待新任务的最长时间
    • 如果设置allowCoreThreadTimeOut为true,核心线程也会受此参数影响
  1. unit(时间单位)
    • keepAliveTime的时间单位
    • 如TimeUnit.SECONDS, TimeUnit.MILLISECONDS等
  1. workQueue(工作队列)
    • 用于存放待执行任务的阻塞队列
    • 常见队列类型:
      • ArrayBlockingQueue:有界队列,FIFO
      • LinkedBlockingQueue:可选有界或无界队列,FIFO
      • SynchronousQueue:不存储元素的队列,每个插入操作必须等待另一个线程调用移除操作
      • PriorityBlockingQueue:优先级队列,允许任务按优先级执行

任务优先级是通过让任务类实现Comparable接口或提供Comparator来确定的。线程池中的线程会按照队列确定的优先级顺序来获取和执行任务,但线程本身的优先级是相同的。

使用PriorityBlockingQueue的例子:

java

class PriorityTask implements Runnable, Comparable<PriorityTask> {
    private final int priority;
    private final String name;
    
    public PriorityTask(int priority, String name) {
        this.priority = priority;
        this.name = name;
    }
    
    @Override
    public void run() {
        System.out.println("执行任务: " + name + ", 优先级: " + priority);
    }
    
    @Override
    public int compareTo(PriorityTask other) {
        // 数字越小优先级越高
        return Integer.compare(this.priority, other.priority);
    }
}

// 使用优先级队列的线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    3, 3, 0L, TimeUnit.MILLISECONDS,
    new PriorityBlockingQueue<>()
);

// 提交不同优先级的任务
executor.execute(new PriorityTask(10, "低优先级任务"));
executor.execute(new PriorityTask(5, "中优先级任务"));
executor.execute(new PriorityTask(1, "高优先级任务"));
  1. threadFactory(线程工厂)
    • 用于创建新线程的工厂
    • 可以自定义线程名称、优先级、守护状态等
    • 默认工厂创建的线程命名为"pool-N-thread-M",为非守护线程,优先级为NORM_PRIORITY
  1. handler(拒绝策略)
    • 当线程池和队列都满了,新提交任务的处理策略
    • 内置四种拒绝策略:
      • AbortPolicy:默认策略,抛出RejectedExecutionException异常
      • CallerRunsPolicy:在调用者线程中执行任务
      • DiscardPolicy:静默丢弃新任务
      • DiscardOldestPolicy:丢弃队列中最早的任务,然后尝试重新提交新任务

线程池工作流程

  1. 如果运行的线程数少于corePoolSize,线程池会创建新线程来处理请求
  2. 如果线程数大于或等于corePoolSize,任务会被放入工作队列
  3. 如果工作队列已满,且运行的线程数少于maximumPoolSize,线程池会创建新线程来处理请求
  4. 如果工作队列已满,且运行的线程数大于或等于maximumPoolSize,拒绝策略会被触发

常见线程池类型

所谓的"线程池类型"实际上指的是不同配置参数组合形成的线程池使用模式。JDK通过Executors工厂类提供了几种预定义的线程池配置,这些只是对ThreadPoolExecutor不同参数组合的封装,主要包括

  1. FixedThreadPool
    • 固定大小线程池,corePoolSize与maximumPoolSize相同
    • 使用无界LinkedBlockingQueue作为工作队列
    • 适用于负载稳定的场景
  1. CachedThreadPool
    • 可缓存线程池,corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE
    • 使用SynchronousQueue作为工作队列
    • 空闲线程保留60秒,适用于执行大量短期异步任务
  1. SingleThreadExecutor
    • 单线程线程池,corePoolSize和maximumPoolSize均为1
    • 使用无界LinkedBlockingQueue作为工作队列
    • 确保任务按提交顺序串行执行
  1. ScheduledThreadPoolExecutor
    • 支持定时及周期性任务执行
    • 使用DelayedWorkQueue作为工作队列
    • 可以设置固定延迟(scheduleWithFixedDelay)或固定速率(scheduleAtFixedRate)执行任务

线程池使用实例

// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                           // 核心线程数
    10,                          // 最大线程数
    60, TimeUnit.SECONDS,        // 空闲线程存活时间
    new LinkedBlockingQueue<>(100), // 工作队列
    new CustomThreadFactory("业务处理线程"),  // 自定义线程工厂
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

// 提交任务
executor.execute(() -> {
    System.out.println("执行任务...");
});

// 提交有返回值的任务
Future<String> future = executor.submit(() -> {
    // 执行任务
    return "任务执行结果";
});

// 获取任务结果(阻塞)
try {
    String result = future.get();
} catch (InterruptedException | ExecutionException e) {
    // 处理异常
}

// 关闭线程池(不再接受新任务,已提交的任务会执行完)
executor.shutdown();

// 立即关闭线程池(尝试停止所有正在执行的任务)
executor.shutdownNow();

线程池监控与调优

  1. 核心监控指标
    • 活跃线程数:getActiveCount()
    • 完成任务数:getCompletedTaskCount()
    • 队列大小:getQueue().size()
    • 线程池大小:getPoolSize()
    • 最大线程数:getLargestPoolSize()
  1. 线程池调优原则
    • CPU密集型任务:线程数 = CPU核心数 + 1
    • IO密集型任务:线程数 = CPU核心数 * (1 + IO耗时/CPU耗时)
    • 混合型任务:需要根据实际性能测试进行调整
    • 避免使用无界队列,防止OOM
    • 根据业务波峰设置合理的maximumPoolSize
    • 根据任务执行时间设置合理的keepAliveTime

线程池最佳实践

  1. 使用ThreadPoolExecutor构造方法创建线程池,避免使用Executors工厂方法(可能导致资源耗尽)
  2. 为线程池中的线程指定有意义的名称,方便问题排查
  3. 设置合理的拒绝策略并进行异常处理
  4. 根据任务类型选择合适的工作队列
  5. 在应用关闭时正确关闭线程池
  6. 避免在线程池中执行死循环任务或阻塞时间过长的任务
  7. 定期检查和监控线程池状态

注意事项

  1. 避免使用过大的线程池,过多的线程会导致线程上下文切换开销增加
  2. 避免使用过小的线程池,可能导致任务队列积压
  3. 避免在任务中捕获但不处理InterruptedException,这会阻止线程响应中断
  4. 在循环中提交大量任务时,注意检查队列积压情况
  5. 关注任务执行时间,避免长时间运行的任务阻塞线程池
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值