写点什么

线程池面试必考

用户头像
叫练
关注
发布于: 2021 年 02 月 23 日
线程池面试必考

你对 Java 线程池了解吗?你有用过线程池吗?那先说下线程池核心参数吧。。。对不起,我回去再看看吧。


image.png


为了一丝体面,我们今天来整理几个面试中常考线程池面试问题吧!


为什么要用线程池?


  1. 线程复用。线程的重复使用是线程池设计的重点,如果需要开启 1000 个线程执行程序,系统会创建 1000 个线程,如果用线程池来执行 1000 个任务,并不需要开启 1000 个线程,只需要设置 corePoolSize 核心线程大小数量,最大线程数量,队列大小即可重复利用线程置换任务,而且 1000 个线程切换效率并不低,也就是说线程越多效率不一定高。所以在多线程环境实际开发中我们推荐用多线程。

  2. 更好的管理线程。ThreadPoolExecutor 可以控制线程数量,根据实际应用场景设置队列数量和饱和策略。


你说下线程池核心参数?


  • corePoolSize : 核心线程大小。线程池一直运行,核心线程就不会停止。

  • maximumPoolSize :线程池最大线程数量。非核心线程数量=maximumPoolSize-corePoolSize

  • keepAliveTime :非核心线程的心跳时间。如果非核心线程在 keepAliveTime 内没有运行任务,非核心线程会消亡。

  • workQueue :阻塞队列。ArrayBlockingQueue,LinkedBlockingQueue 等,用来存放线程任务。

  • defaultHandler :饱和策略。

  • ThreadFactory :线程工厂。新建线程工厂。


execute 任务添加流程?



image.png


  1. 线程池执行 execute/submit 方法向线程池添加任务,当任务小于核心线程数 corePoolSize,线程池中可以创建新的线程。

  2. 当任务大于核心线程数 corePoolSize,就向阻塞队列添加任务。

  3. 如果阻塞队列已满,需要通过比较参数 maximumPoolSize,在线程池创建新的线程,当线程数量大于 maximumPoolSize,说明当前设置线程池中线程已经处理不了了,就会执行饱和策略。


饱和策略知道吗?


上图我们说过,当线程数量大于 maximumPoolSize,就会执行饱和策略。ThreadPoolExecutor 类中一共有 4 种饱和策略。通过实现 RejectedExecutionHandler 接口。

  • AbortPolicy : 线程任务丢弃报错。默认饱和策略。

  • DiscardPolicy : 线程任务直接丢弃不报错。

  • DiscardOldestPolicy : 将 workQueue 队首任务丢弃,将最新线程任务重新加入队列执行。

  • CallerRunsPolicy :线程池之外的线程直接调用 run 方法执行。


下面我们在代码中看下饱和策略使用方式。

import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.CountDownLatch;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;
/** * @author :jiaolian * @date :Created in 2021-02-20 16:28 * @description:线程池丢弃策略 * @modified By: * 公众号:叫练 */public class AbortTest {
//线程数量 private static final int THREAD_COUNT = 50;
private static final CountDownLatch COUNT_DOWN_LATCH = new CountDownLatch(THREAD_COUNT);
private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(1);
//线程池丢弃策略 public static void main(String[] args) throws InterruptedException {
//新建一个线程池 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 3,5,1, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(20),new ThreadPoolExecutor.AbortPolicy());
//DiscardPolicy 丢弃 //AbortPolicy 丢弃报错 //DiscardOldestPolicy 将队列对首的任务丢弃,执行当前线程任务 //CallerRunsPolicy 直接调用run方法
//提交线程 for (int i=0; i<THREAD_COUNT; i++) { threadPoolExecutor.execute(()->{ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" execute!"+ATOMIC_INTEGER.getAndIncrement()); COUNT_DOWN_LATCH.countDown(); }); }
COUNT_DOWN_LATCH.await(); //关闭线程 threadPoolExecutor.shutdown();

}}
复制代码

如上代码:核心线程数量是 3,最大线程数量是 5,阻塞队列是 20,共提交 50 个线程,这里饱和策略用的是 AbortPolicy,分析执行线程池过程,线程池中首先开启 3 个核心线程 Worker,发现 3 个线程处理不了 50 个线程任务,于是线程池就向阻塞队列添加任务,发现还是阻塞队列也容纳不下 50 个任务,于是又增加至 2 个线程同时运行线程任务,一共是 5 个线程同时运行任务,此时线程池中共有 25 个任务会被执行,还有 25 个任务会被丢弃,因为我们用的是 AbortPolicy 饱和策略,会报错,截部分图如下划红线所示。一共执行了 25 个任务。其他几种策略大家可以参照执行。


image.png


你平时线程池怎么用的?


Excutors.newSingleThreadExecutor :1 个 corePoolSize,LinkedBlockingQueue 队列无限大,当创建无数个线程,队列无限长,可能出现 OOM 内存溢出。单一线程。

Executors.newCachedThreadPool :0 个 corePoolSize,Interger.MAX_VALUE 个最大线程数,当创建无数个线程,可能出现 OOM 内存溢出。适用小而多线程。

Executors.newFixedThreadPool :n 个 corePoolSize,n 个最大线程个数,LinkedBlockingQueue 阻塞队列,当创建无数个线程,队列无限长,可能出现 OOM 内存溢出。适用固定线程。

Executors.newScheduledThreadPool :n 个 corePoolSize,Interger.MAX_VALUE 个最大线程数,当创建无数个线程,可能出现 OOM 内存溢出。


源码中线程池是怎么复用线程的?


源码中 ThreadPoolExecutor 中有个内置对象 Worker,每个 worker 都是一个线程,worker 线程数量和参数有关,每个 worker 会 while 死循环从阻塞队列中取数据,通过置换 worker 中 Runnable 对象,运行其 run 方法起到线程置换的效果,这样做的好处是避免多线程频繁线程切换,提高程序运行性能。


总结


今天我们介绍了线程池中面试中几个重要的面试点,整理出来希望能对你有帮助,写的比不全,同时还有许多需要修正的地方,希望亲们加以指正和点评,喜欢的请点赞加关注哦。点关注,不迷路,我是叫练【公众号】,微信号【jiaolian123abc】边叫边练。


发布于: 2021 年 02 月 23 日阅读数: 19
用户头像

叫练

关注

我是叫练,边叫边练 2020.06.11 加入

Java高级工程师,熟悉多线程,JVM

评论

发布
暂无评论
线程池面试必考