乐趣区

关于面试:线程池面试必考

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

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

为什么要用线程池?


  1. 线程复用。线程的重复使用是线程池设计的重点,如果须要开启 1000 个线程执行程序,零碎会创立 1000 个线程,如果用线程池来执行 1000 个工作,并不需要开启 1000 个线程,只须要设置 corePoolSize 外围线程大小数量,最大线程数量,队列大小即可反复利用线程置换工作,而且 1000 个线程切换效率并不低,也就是说线程越多效率不肯定高。所以在多线程环境理论开发中咱们举荐用多线程。
  2. 更好的治理线程。ThreadPoolExecutor 能够控制线程数量,依据理论利用场景设置队列数量和饱和策略。

你说下线程池外围参数?


  • corePoolSize:外围线程大小。线程池始终运行,外围线程就不会进行。
  • maximumPoolSize:线程池最大线程数量。非核心线程数量 =maximumPoolSize-corePoolSize
  • keepAliveTime:非核心线程的心跳工夫。如果非核心线程在 keepAliveTime 内没有运行工作,非核心线程会沦亡。
  • workQueue:阻塞队列。ArrayBlockingQueue,LinkedBlockingQueue 等,用来寄存线程工作。
  • defaultHandler:饱和策略。
  • ThreadFactory:线程工厂。新建线程工厂。

execute 工作增加流程?


  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 个工作。其余几种策略大家能够参照执行。

你平时线程池怎么用的?


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

退出移动版