Java線程池

線程池的優(yōu)勢:

  1. 通過復(fù)用已有的線程,降低線程創(chuàng)建的銷毀的系統(tǒng)開銷
  2. 提高響應(yīng)速度,復(fù)用已有的線程避免了創(chuàng)建線程的開銷
  3. 方便線程數(shù)量的管控,如果創(chuàng)建的線程過多,咋可能導(dǎo)致系統(tǒng)化新能的下降或者oom的發(fā)生。、
  4. 線程池提供了定時等功能,并且方便創(chuàng)建

我們可以使用new ThreadPoolExecutor()來創(chuàng)建一個線程池

    public ThreadPoolExecutor(
          int corePoolSize,
          int maximumPoolSize,
          long keepAliveTime,
          TimeUnit unit,
          BlockingQueue<Runnable> workQueue,
          ThreadFactory threadFactory,
          RejectedExecutionHandler handler)
  1. corePoolSize 這個參數(shù)意思是核心線程的數(shù)量,他們一直存在于線程池中,即使處于閑置狀態(tài)也不會被銷毀,除非設(shè)置了allowCoreThreadTimeOut這個參數(shù),則在閑置狀態(tài)超過了這個參數(shù)keepAliveTime 則被銷毀
  2. maximumPoolSize 線程池中的最大線程數(shù)量,如果活動的線程數(shù)超過這個值的話,后續(xù)的線程就會被阻塞。 最大數(shù)量指的是核心線程數(shù)量和非核心線程數(shù)量
  3. keepAliveTime 非核心線程閑置存在的時長,意思當非核心線程閑置時間超過這個值就會被回收
  4. TimeUnit 是keepAliveTime的時間單位,是個枚舉類型,包含TimeUnit.MILLISECONDS 毫秒,SECONDS秒,天等等
  5. workQueue 阻塞隊列。 所有提交的Runnable線程都存放到該隊列中,常用的阻塞隊列有:
    image
  6. threadFactory 線程 工廠,為線程池提供線程的創(chuàng)建,一般使用默認的即可Executors.defaultThreadFactory()
  7. handler 當任務(wù)隊列已滿,并且線程池達到了最大線程數(shù)的時候規(guī)定了接下來的線程該如何處理。
    image

    ThreadPoolExecutor提供了多鐘構(gòu)造方法,我們可以使用他默認的一些方法來創(chuàng)建,比如:
ThreadPoolExecutor executor = new   
ThreadPoolExecutor(  
10, 10, 1000, TimeUnit.SECONDS,  
new LinkedBlockingDeque<>());

ThreadPoolExecutor 提供了execute和submit兩個方法提交線程任務(wù),比如:

        executor.submit(()->{});
        executor.execute(()->{});

但是submit提供了一分返回值,所以如果們用Callable的話,那么我們就可以獲得一個返回值,如果不知道Callable請看我的另外一篇文章 《Java第三種線程創(chuàng)建方法》

    Future<String> submit = executor.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "";
        }
    });
    
    submit.get();
    如果使用Lambda更簡單:
    Future<String> submit = executor.submit(() -> "")

submit.get(); 可以得到call中返回的值。

其實系統(tǒng)為我們提供了集中線程池的創(chuàng)建,所以不用直接使用ThreadPoolExecutor這個類來創(chuàng)建線程池:

newFixedThreadPool

Executors.newFixedThreadPool(2);
//源碼
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

newFixedThreadPool,固定線程數(shù)量的線程池,核心線程池數(shù)和非核心線程數(shù)量相等并且由自己定義,不存在超時機制,任務(wù)隊列理論是上可以無限大

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());

可以看到 核心線程數(shù)為0,而線程池的最大數(shù)量可以認為無限大,超時時間60秒,并且是不緩存線程的SynchronousQueue隊列,所以可以來多少線程就去執(zhí)行多少線程,沒有任務(wù)了60秒之后就全部銷毀

ScheduledExecutorService

  public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
      new DelayedWorkQueue());
}

定時執(zhí)行線程任務(wù)的線程池:核心線程數(shù)由自己定義,最大線程數(shù)量為無限大,超時間為0秒,所以非核心線程閑置立刻被銷毀,用到了延遲隊列,可以設(shè)置線程延遲多少時間執(zhí)行,或者多長時間重復(fù)一次,比如:

scheduledExecutorService.schedule(() -> {}, 10, TimeUnit.SECONDS);
scheduledExecutorService.scheduleAtFixedRate(()->{}, 20, 10, TimeUnit.SECONDS);

第一表示10秒之后執(zhí)行線程,第二個表示20秒之后每10秒執(zhí)行一次線程

newSingleThreadExecutor

   public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>()));
    }

可以看到核心線程數(shù)和最大線程數(shù)都是1,其實就是單線程了,所以也就不用考慮并發(fā)啊什么的了。

線程池的執(zhí)行過程

線程池執(zhí)行的過程也挺有意思:

  1. 提交一個線程,如果核心線程數(shù)量沒有滿的話,就啟動一個線程來執(zhí)行。
  2. 如果核心線程已滿,那么就插入到阻塞隊列里面去
  3. 如果阻塞隊列已經(jīng)滿的話,就啟動一個非核心線程
  4. 如果非核心線程也滿了,那就調(diào)用handler,報異?;蛘呓o替換掉了
    原本我以為,如果核心線程已滿,就會立即啟動非核心線程來執(zhí)行線程,實際上是加到阻塞隊列里面,只有阻塞隊列滿了之后,才會啟動非核心線程??梢院唵蔚呐e個例子:
ThreadPoolExecutor service = new ThreadPoolExecutor(2, 10,   
1000, TimeUnit.SECONDS, new LinkedBlockingDeque<>());

service.execute(() -> {
    try {
        Thread.sleep(2000);
        System.out.println(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
service.execute(() -> {
    try {
        Thread.sleep(2000);
        System.out.println(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
service.execute(() -> {
    System.out.println(3);
});

上面定義了一個2個核心線程數(shù)量和2個非核心線程數(shù)量的線程池,然后提交3個線程,前兩個沉睡2秒,結(jié)果總是:1 3 2
所以是等到核心線程執(zhí)行結(jié)束又執(zhí)行的第3個線程。
那么對于ScheduledExecutorService 這個線程池如果也是符合這個理論的話,豈不是設(shè)置的定時可能永遠不會執(zhí)行到了么? 我們做個試驗:

ScheduledExecutorService service = Executors.newScheduledThreadPool(10);

service.schedule(()->{

    try {
        Thread.sleep(3000);
        System.out.println(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, 0, TimeUnit.MILLISECONDS );


service.schedule(()->{

    try {
        Thread.sleep(3000);
        System.out.println(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, 0, TimeUnit.MILLISECONDS );

service.schedule(()-> System.out.println(3), 1000, TimeUnit.MILLISECONDS );

但是結(jié)果卻始終是 3 1 2. 所以對于這個定時的線程池來說這個理論并不符合

最后當應(yīng)用退出,別忘了關(guān)系線程池:

    service.shutdown();
    service.shutdownNow();
    service.isShutdown();

其中isShutdown表示線程池關(guān)閉是否完成。
shutdown 表示中斷所有沒有執(zhí)行的線程任務(wù)
shutdownNow表示中斷執(zhí)行所有任務(wù)線程,包括正在執(zhí)行的線程,他會有一個返回值,返回的是那些沒有執(zhí)行的線程列表

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 線程池是什么? 線程池用于多線程處理中,它可以根據(jù)系統(tǒng)的情況,可以有效控制線程執(zhí)行的數(shù)量,優(yōu)化運行效果。線程池做的...
    懶癌正患者閱讀 2,851評論 2 80
  • 本文已獨家授權(quán) 鴻洋( hongyangAndroid) 公眾號發(fā)布! 前言: 本篇文章主要介紹的是Java(...
    騎小豬看流星閱讀 32,327評論 36 340
  • 4 地鐵快速行進的聲音因為車廂里幾乎沒有乘客而顯得特別的生冷。 柏曉雯又看了看老人。老人仍舊保持著剛才的姿勢,一動...
    北岸夜雨閱讀 3,541評論 1 3
  • 我們都讀過紀伯倫的那首詩,孩子是分離的獨立個體,要尊重孩子的生命體驗。但要真正的做到,何其難也。 七七...
    筱荀閱讀 348評論 0 0
  • 看火爐山禿頂有感 改造致富 火爐山下山兒禿, 望眼欲穿是房屋。 城市改革確實好, 那日荒山今富庶。 山水不...
    康真閱讀 267評論 0 0

友情鏈接更多精彩內(nèi)容