Java线程池,看这一篇足够

发布时间:2024年01月24日

Java线程池

上一篇《@Async注解的注意事项》说到@Async注解要配合自定义线程池一起使用,这一节说下Java的线程池。

1. Executors提供6个线程池快捷创建方式

    public static void main(String[] args) {
        // 定长线程池,核心线程数和最大线程数一致
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // 缓存线程池,核心线程数为0,最大线程数Integer最大值,线程存活时间超长
        ExecutorService executorService1 = Executors.newCachedThreadPool();
        // 定时线程池,可以设置执行时间
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
        // 单线程线程池,核心线程和最大线程数都为1
        ExecutorService executorService2 = Executors.newSingleThreadExecutor();
        // 核心线程数为1的定时线程池,可以执行定时任务
        ScheduledExecutorService scheduledExecutorService1 = Executors.newSingleThreadScheduledExecutor();
        // 窃取线程池,并行执行线程,依赖cpu个数
        ExecutorService executorService3 = Executors.newWorkStealingPool(3);
    }

通过调试依次看下各个线程池的实际参数

newFixedThreadPool,定长线程池

在这里插入图片描述

可见,corePoolSize和maxinumPoolSize数是一样的,说明线程不会销毁,且不会动态扩容。灵活性较差

newCachedThreadPool,缓存线程池

在这里插入图片描述

corePoolSize为0,maxinumPoolSize为max integer,重点是keepAliveTime为600亿毫秒,线程一旦创建几乎不会销毁,容易造成OOM。

newScheduledThreadPool,定时线程池

在这里插入图片描述

maxinumPoolSize为max integer,可能造成OOM。主要特点是创建的ScheduledThreadPoolExecutor实现了ScheduledExecutorService接口,其中的schedule等方法可以设置定时执行任务。

在这里插入图片描述

newSingleThreadExecutor,单线程线程池

在这里插入图片描述

corePoolSize和maxinumPoolSize都为1,说明单个线程执行任务,未执行的任务只能堆叠在workQueue。

newSingleThreadScheduledExecutor,单线程定时线程池

在这里插入图片描述

事实上就是newScheduledThreadPool,只是corePoolSize设置为1

在这里插入图片描述

newWorkStealingPool,窃取线程池

在这里插入图片描述

这个线程池比较特殊,它是java8才提出来的,不是通过ThreadPoolExecutor创建,没有corePoolSize等参数。且线程是并行进行的,即依赖处理器进行。

我觉得下面这篇文章对我有帮助,可以阅读一下

WorkStealingPool的使用简析

以上就是通过Executors创建的6种线程池

2. ThreadPoolExecutor的7大参数

通过Executors创建的6种线程池,除了java8提出的newWorkStealingPool,其他都是直接或者间接(子类)创建

ThreadPoolExecutor类实现的。

在这里插入图片描述

ThreadPoolExecutor主要有7大参数

在这里插入图片描述

  • corePoolSize,核心线程数,线程池默认线程数,线程池创建完成时,会初始化的线程数量,当有任务进来时,通过调度线程执行任务。

  • maxinumPoolSize,最大线程数,最大线程数必须大于或等于核心线程数,核心线程正在执行任务,而有新任务进来,且当下线程数量未达到最大线程数时,会新建线程放进线程池,再用新线程执行任务。

  • keepAliveTime,存活时间,除开核心线程外的线程,也就是大于核心线程数的部分线程,如果没有任务调度该线程执行,当该线程的空闲时间达到keepALiveTime值时,会被销毁。

  • unit,时间单位,keepAliveTime的单位,一般为毫秒

  • workQueue,工作队列,也称为等待队列,任务调用时先进入工作队列,然后等待空闲线程调度。

  • threadFactory,线程工厂,指定创建线程的方式,通过线程工厂创建和销毁线程,一般使用 Executors.defaultThreadFactory() 足够,不用自定义创建该工厂类。

  • handler,拒绝策略,该类主要针对当工作队列满了之后,新的任务进来,拒绝存放在工作队列,执行的操作。其中rejectedExecution方法就是拒绝的处理方法。

    ThreadPoolExecutor提供了默认四种拒绝策略。(如果不理解可以看下源码,很简单的逻辑)

    • CallerRunsPolicy,从哪来,往哪走,由提交执行任务。
    • AbortPolicy,直接报异常
    • DiscardPolicy,什么都不做,rejectedExecution这个方法直接留空,哈哈
    • DiscardOldestPolicy,workQueue最后的任务直接踢掉,然后插入本任务

3. 自定义线程池

在阿里开发手册中明令禁止使用Executors创建线程池,通过以上第一点介绍的6种线程池可以看出,Executors提供的线程池中有些maxinumPoolSize设置为max integer,或者workQueue的容量设置为max integer(如newFixedThreadPool和newSingleThreadExecutor中,workQueue使用LinkedBlockingQueue,其size为Integer.MAX_VALUE),这存在内存溢出风险。

因而在实际开发中,最好自定义线程池。只需要实例化ThreadPoolExecutor类即可,所以自定义线程池实质上就是配置好上述第二点中的7大参数。

    public ThreadPoolExecutor myThreadExecutor(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                10,
                20,
                60,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        return threadPoolExecutor;
    }

最后,@Async配合自定义线程池使用,可以看我上篇文章《@Async注解的注意事项

至此,全篇结束

文章来源:https://blog.csdn.net/weixin_42510217/article/details/135802587
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。