Java 线程池使用 Executors 与 ThreadPoolExecutor 及线程大小配置判断方法

参考

介绍

Executors 工具类创建线程实现是调用的 ThreadPoolExecutor,但是隐藏了部分细节和参数设置。并且阿里巴巴代码规范也禁止使用 Executors 工具类创建线程池。

创建方法 描述
Executors 工具类三种方法创建线程 堵塞等待执行(这里不确定,可能有其他方法可以指定策略来堵塞或抛弃任务)
ThreadPoolExecutor 根据配置的拒绝策略来执行

Executors 方法

方法名 描述
Executors.newSingleThreadExecutor() 单一线程
Executors.newFixedThreadPool(int nThreads) 指定线程数量
Executors.newCachedThreadPool() 无限制,自动扩容与缩容(最大21亿)

ThreadPoolExecutor 介绍

  1. 参数列表

    参数名 描述
    int corePoolSize 核心线程数(不会被关闭)
    int maximumPoolSize 最大线程数(当队列满了之后,就会适当开启线程)
    long keepAliveTime 当前线程数大于核心线程数,并且某些线程空闲一定时间后,会被关闭
    TimeUnit unit 超时时间单位
    BlockingQueue workQueue 指定使用的阻塞队列类型与设置队列的大小
    ThreadFactory threadFactory 线程工厂,主要用来创建线程,默认为正常优先级、非守护线程
    RejectedExecutionHandler handler 拒绝策略,有四种
  2. 拒绝策略

    策略名 描述
    ThreadPoolExecutor.AbortPolicy 满了(最大线程数+队列大小)就报错,但是已经进入的任务会执行完毕,这里 shutdown() 方法要放在 finally 中
    ThreadPoolExecutor.CallerRunsPolicy 满了(最大线程数+队列大小)拒绝/丢弃,直接在execute方法的调用线程中运行被拒绝的任务(main线程)
    ThreadPoolExecutor.DiscardOldestPolicy 满了(最大线程数+队列大小)就拒绝/丢弃最早进入并且未处理的任务
    ThreadPoolExecutor.DiscardPolicy 满了就拒绝/丢弃

Executors 不推荐

package pool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @Author 夏秋初
 * @Date 2022/3/3 14:22
 */
public class Test {
    public static void main(String[] args) {
        // 单一线程
//        ExecutorService executorService = Executors.newSingleThreadExecutor();
        // 固定线程数量
//        ExecutorService executorService = Executors.newFixedThreadPool(2);
//         最大21亿
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 10; i++) {
            executorService.execute(()->{
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());
            });
        }
        // 必须手动关闭
        executorService.shutdown();
    }
}

ThreadPoolExecutor 推荐

package pool;

import java.util.concurrent.*;

/**
 * @Author 夏秋初
 * @Date 2022/3/3 14:22
 */
public class Test2 {
    public static void main(String[] args) {
        // 线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                // 核心线程数(不会被关闭)
                2,
                // 最大线程数(当队列满了之后,就会适当开启线程)
                5,
                // 当前线程数大于核心线程数,并且某些线程空闲一定时间后,会被关闭
                10,
                // 超时时间单位,当前例子是 10s
                TimeUnit.SECONDS,
                // 指定使用的阻塞队列类型与设置队列的大小
                new LinkedBlockingDeque<Runnable>(3),
                // 线程工厂,主要用来创建线程,默认为正常优先级、非守护线程
                Executors.defaultThreadFactory(),
                /**
                 * 拒绝策略
                 * ThreadPoolExecutor.AbortPolicy 满了就报错,但是已经进入的任务会执行完毕,这里 shutdown() 方法要放在 finally 中
                 * ThreadPoolExecutor.CallerRunsPolicy 满了丢弃,直接在execute方法的调用线程中运行被拒绝的任务
                 * ThreadPoolExecutor.DiscardOldestPolicy 满了就丢弃最早进入并且未处理的任务
                 * ThreadPoolExecutor.DiscardPolicy 满了就丢弃
                 */
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );

        try{
            for (int i = 0; i < 20; i++) {
                final int temp = i;
                threadPoolExecutor.execute(()->{
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"  "+temp);
                });
            }
        }finally {
            // 必须手动关闭
            threadPoolExecutor.shutdown();
        }
    }
}


IO 密集型与 CPU 密集型

代码中不建议把线程数写成固定的,可以通过 Runtime.getRuntime().availableProcessors() 来获取 CPU 线程数量

类型 数量值 原因
CPU 密集型 最大线程数=CPU线程数+1 对于计算要求高,让线程尽快执行
IO 密集型 最大线程数=CPU线程数*2 占用资源,容易堵塞,耗时比较久,可以给别的线程运行机会
posted @ 2022-03-03 15:07  夏秋初  阅读(409)  评论(0编辑  收藏  举报