zoukankan      html  css  js  c++  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个任务。其他几种策略大家可以参照执行。

    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】边叫边练。

  • 相关阅读:
    Python——五分钟带你弄懂迭代器与生成器,夯实代码能力
    LeetCode37 使用回溯算法实现解数独,详解剪枝优化
    LeetCode 33,在不满足二分的数组内使用二分的方法
    丰富图文详解B-树原理,从此面试再也不慌
    看完这篇让你高数不挂科之——泰勒公式
    数据结构——动手实战双向链表
    你听说过JMX么
    【网络安全】CSRF攻击详解
    【开发工具】本机安装的JDK8,启动IDEA2019没反应
    Java开发过程中的常用工具类库
  • 原文地址:https://www.cnblogs.com/jiaolian/p/14434593.html
Copyright © 2011-2022 走看看