那下一个问题,面试官接着问:
“ Java里面的线程池都有哪些参数?”
我当时就记得“核心线程数、最大线程数”,其他的应该还有5个,支支吾吾答不上来,直接就凉了。
线程池是用来管理线程的,避免频繁创建和销毁线程带来的性能开销。
类比一下工厂招工:
- 不用线程池
来一个任务就招一个新工人(创建线程),任务干完就辞退(销毁线程),频繁招工辞退,效率极低,还费钱;
- 用线程池
工厂提前招好一批固定工人(核心线程),任务来了直接让固定工人干,固定工人忙不过来就把任务放待办队列(工作队列),队列也满了就临时招几个临时工(非核心线程),临时工没活干了过段时间就辞退,效率高,还省钱。
线程池的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:拒绝策略
工厂的任务太多了,正式工、临时工、待办队列都满了,怎么处理新任务,就是靠这个参数定义的。
线程池的执行流程
- 提交新任务
工厂来了一个新任务;
- 判断核心线程数
先看正式工(corePoolSize)有没有满,没满的话,直接招一个正式工(创建核心线程)执行任务;
- 判断工作队列
如果正式工满了,就看待办队列(workQueue)有没有满,没满的话,把任务放进队列,等正式工有空了再拿;
- 判断最大线程数
如果队列也满了,就看正式工+临时工的总数(maximumPoolSize)有没有满,没满的话,招一个临时工(创建非核心线程)执行任务;
- 执行拒绝策略
如果最大线程数也满了,就执行拒绝策略(handler),处理这个新任务;
- 任务执行完
线程执行完任务,会从队列里拿下一个任务继续干;
- 空闲线程回收
如果是临时工(非核心线程),没活干的时间超过了 keepAliveTime,就会被回收(辞退)。
4种默认拒绝策略
| 拒绝策略 | 作用 | 适用场景 |
|---|---|---|
| AbortPolicy(默认) | 直接抛出RejectedExecutionException异常,拒绝执行新任务 | 对数据一致性要求高的场景,任务不能丢,必须让调用者知道任务被拒绝了 |
| CallerRunsPolicy | 由提交任务的线程(调用者线程)自己执行这个任务 | 对性能要求不高,任务不能丢的场景,比如日志记录 |
| DiscardPolicy | 直接丢弃新任务,不抛异常,也不做任何处理 | 对数据一致性要求不高,丢几个任务没关系的场景,比如非核心的监控数据上报 |
| DiscardOldestPolicy | 丢弃队列里最老的任务(等待时间最长的任务),然后把新任务放进队列 | 对数据一致性要求不高,但想尽量执行新任务的场景 |
ThreadPoolExecutor 的构造方法
|
|