Loading...

【面试真题拆解】Java里面的线程池都有哪些参数?

那下一个问题,面试官接着问:

“ Java里面的线程池都有哪些参数?”

我当时就记得“核心线程数、最大线程数”,其他的应该还有5个,支支吾吾答不上来,直接就凉了。

线程池是用来管理线程的,避免频繁创建和销毁线程带来的性能开销。

类比一下工厂招工:

  1. 不用线程池

来一个任务就招一个新工人(创建线程),任务干完就辞退(销毁线程),频繁招工辞退,效率极低,还费钱;

  1. 用线程池

工厂提前招好一批固定工人(核心线程),任务来了直接让固定工人干,固定工人忙不过来就把任务放待办队列(工作队列),队列也满了就临时招几个临时工(非核心线程),临时工没活干了过段时间就辞退,效率高,还省钱。

线程池的7个核心参数

Java线程池的核心类是 ThreadPoolExecutor,它的构造方法有7个参数:

1. corePoolSize:核心线程数

工厂的正式工数量

正式工是工厂的“长期工”,默认情况下,就算没活干,也不会被辞退;

任务来了,先让正式工干,正式工没满的话,直接创建正式工执行任务。

2. maximumPoolSize:最大线程数

工厂的正式工+临时工的总数量

正式工忙不过来,待办队列也满了,就会招临时工;

正式工+临时工的总数,不能超过最大线程数。

3. keepAliveTime:空闲线程存活时间

工厂的临时工没活干的时候,多久会被辞退。

只有临时工(非核心线程)会被回收,正式工(核心线程)默认不会被回收;

但是,如果设置了 allowCoreThreadTimeOut(true),正式工没活干了,过了这个时间也会被辞退。

4. unit:时间单位

空闲线程存活时间的单位

比如 TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分钟)、TimeUnit.HOURS(小时)。

5. workQueue:工作队列

工厂的待办任务队列

正式工(corePoolSize)都在忙,新任务来了就先放进待办队列,等正式工有空了再从队列里拿任务干。

6. threadFactory:线程工厂

给工厂的工人起名字的

用来创建新线程,给线程设置名字、优先级、是否是守护线程等。

7. handler:拒绝策略

工厂的任务太多了,正式工、临时工、待办队列都满了,怎么处理新任务,就是靠这个参数定义的。

线程池的执行流程

  1. 提交新任务

工厂来了一个新任务;

  1. 判断核心线程数

先看正式工(corePoolSize)有没有满,没满的话,直接招一个正式工(创建核心线程)执行任务;

  1. 判断工作队列

如果正式工满了,就看待办队列(workQueue)有没有满,没满的话,把任务放进队列,等正式工有空了再拿;

  1. 判断最大线程数

如果队列也满了,就看正式工+临时工的总数(maximumPoolSize)有没有满,没满的话,招一个临时工(创建非核心线程)执行任务;

  1. 执行拒绝策略

如果最大线程数也满了,就执行拒绝策略(handler),处理这个新任务;

  1. 任务执行完

线程执行完任务,会从队列里拿下一个任务继续干;

  1. 空闲线程回收

如果是临时工(非核心线程),没活干的时间超过了 keepAliveTime,就会被回收(辞退)。

4种默认拒绝策略

拒绝策略 作用 适用场景
AbortPolicy(默认) 直接抛出RejectedExecutionException异常,拒绝执行新任务 对数据一致性要求高的场景,任务不能丢,必须让调用者知道任务被拒绝了
CallerRunsPolicy 由提交任务的线程(调用者线程)自己执行这个任务 对性能要求不高,任务不能丢的场景,比如日志记录
DiscardPolicy 直接丢弃新任务,不抛异常,也不做任何处理 对数据一致性要求不高,丢几个任务没关系的场景,比如非核心的监控数据上报
DiscardOldestPolicy 丢弃队列里最老的任务(等待时间最长的任务),然后把新任务放进队列 对数据一致性要求不高,但想尽量执行新任务的场景

ThreadPoolExecutor 的构造方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.concurrent.*;

/**
 * 线程池示例
 */
public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 定义线程池的7个核心参数
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                5, // corePoolSize:核心线程数(5个正式工)
                10, // maximumPoolSize:最大线程数(5个正式工+5个临时工)
                60, // keepAliveTime:空闲线程存活时间(60秒)
                TimeUnit.SECONDS, // unit:时间单位(秒)
                new ArrayBlockingQueue<>(100), // workQueue:有界队列(容量100)
                Executors.defaultThreadFactory(), // threadFactory:默认线程工厂
                new ThreadPoolExecutor.AbortPolicy() // handler:默认拒绝策略(抛异常)
        );

        // 提交任务
        for (int i = 0; i < 200; i++) {
            final int taskId = i;
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务:" + taskId);
            });
        }

        // 关闭线程池
        threadPool.shutdown();
    }
}
Code Road Record