線程池面試必考

你對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

相關文章
相關標籤/搜索