线程池面试必考

你对Java线程池了解吗?你有用过线程池吗?那先说下线程池核心参数吧。。。对不起,我回去再看看吧。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个任务。其余几种策略你们能够参照执行。ide

image.png


你平时线程池怎么用的?


  • Excutors.newSingleThreadExecutor :1个corePoolSize,LinkedBlockingQueue队列无限大,当建立无数个线程,队列无限长,可能出现OOM内存溢出。单一线程。
  • Executors.newCachedThreadPool :0个corePoolSize,Interger.MAX_VALUE最大线程数,建立无数个线程,可能出现OOM内存溢出。适用小而多线程。
  • Executors.newFixedThreadPool :ncorePoolSize,n个最大线程个数,LinkedBlockingQueue阻塞队列,建立无数个线程,队列无限长,可能出现OOM内存溢出。适用固定线程。
  • Executors.newScheduledThreadPool :ncorePoolSize,Interger.MAX_VALUE个最大线程数,建立无数个线程,可能出现OOM内存溢出。


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


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


总结


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

相关文章
相关标签/搜索