线程池简单整理

本文深入解析Java线程池的定义、ThreadPoolExecutor的构造参数、执行原理以及四种常见线程池类型。讨论了阿里禁止使用Executors创建线程池的原因,并提供了线程池参数的合理设定方法。此外,还涵盖了线程池的拒绝策略,如AbortPolicy、DiscardPolicy等,并给出了优化建议。
摘要由CSDN通过智能技术生成

1. 线程池的定义

管理一组工作线程。通过线程池复用线程有以下几点优点:

  • 减少资源创建 => 减少内存开销,创建线程占用内存

  • 降低系统开销 => 创建线程需要时间,会延迟处理的请求

  • 提高稳定稳定性 => 避免无限创建线程引起的OutOfMemoryError简称OOM

2、ThreadPoolExecutor对象


public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

 

构造函数参数说明:

  • corePoolSize => 线程池核心线程数量

  • maximumPoolSize => 线程池最大数量

  • keepAliveTime => 空闲线程存活时间

  • unit => 时间单位

  • workQueue => 线程池所使用的缓冲队列

  • threadFactory => 线程池创建线程使用的工厂

  • handler => 线程池对拒绝任务的处理策略

线程池执行原理

图片

 

2、线程池的四种创建方式

(1)newCachedThreadPool  创建一个可缓存的线程池,如果线程池的长度超过需要处理的,可灵活回收空闲线程,

(2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

(3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

阿里禁止使用Executors去创建线程池原因

  • FixedThreadPool和SingleThreadExecutor => 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而引起OOM异常

  • CachedThreadPool => 允许创建的线程数为Integer.MAX_VALUE,可能会创建大量的线程,从而引起OOM异常

代码实践

public class ThreadPoolExecutorTest {
    public static void main(String[] args) {

        try {
            ExecutorService es = Executors.newCachedThreadPool();
            Long i = 0L;
            while (true) {
                es.submit(new MyThread(++i));
            }
        } catch (Exception e) {
            e.getCause();
            e.printStackTrace();
            return;
        }
    }

    static class MyThread extends Thread{
        private Long num;

        public MyThread(Long num) {
            this.num = num;
        }

        @Override
        public void run() {
           System.out.println("这是第=="+num+"===线程");
        }
    }
    
}

 

设置java虚拟机运行内存

图片

 

报错信息

 

如何定义线程池参数

  • CPU密集型 => 线程池的大小推荐为CPU数量 + 1,CPU数量可以根据Runtime.availableProcessors方法获取

  • IO密集型 => CPU数量 * CPU利用率 * (1 + 线程等待时间/线程CPU时间)

  • 混合型 => 将任务分为CPU密集型和IO密集型,然后分别使用不同的线程池去处理,从而使每个线程池可以根据各自的工作负载来调整

  • 阻塞队列 => 推荐使用有界队列,有界队列有助于避免资源耗尽的情况发生

  • 拒绝策略 => 默认采用的是AbortPolicy拒绝策略,直接在程序中抛出RejectedExecutionException异常【因为是运行时异常,不强制catch】,这种处理方式不够优雅。处理拒绝策略有以下几种比较推荐:

    • 在程序中捕获RejectedExecutionException异常,在捕获异常中对任务进行处理。针对默认拒绝策略

    • 使用CallerRunsPolicy拒绝策略,该策略会将任务交给调用execute的线程执行【一般为主线程】,此时主线程将在一段时间内不能提交任何任务,从而使工作线程处理正在执行的任务。此时提交的线程将被保存在TCP队列中,TCP队列满将会影响客户端,这是一种平缓的性能降低

    • 自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可

    • 如果任务不是特别重要,使用DiscardPolicyDiscardOldestPolicy拒绝策略将任务丢弃也是可以

拒绝策略:

hreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务

ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

 线程池中默认的拒绝策略:ThreadPoolExecutor.AbortPolicy。
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值