Executors 創(chuàng)建線程池

一、概述

  • 通過java.util.concurrent.ExecutorService接口對象來執(zhí)行任務,該接口對象通過工具類java.util.concurrent.Executors的靜態(tài)方法來創(chuàng)建。
  • Executors此包中所定義的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 類的工廠和實用方法。
  • ExecutorService提供了管理終止的方法,以及可為跟蹤一個或多個異步任務執(zhí)行狀況而生成 Future 的方法。 可以關(guān)閉 ExecutorService,這將導致其停止接受新任務。關(guān)閉后,執(zhí)行程序?qū)⒆詈蠼K止,這時沒有任務在執(zhí)行,也沒有任務在等待執(zhí)行,并且無法提交新任務。

Java通過Executors創(chuàng)建線程池,目前有

  • newCachedThreadPool:創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
  • newFixedThreadPool: 創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。
  • newScheduledThreadPool: 創(chuàng)建一個定長線程池,支持定時及周期性任務執(zhí)行。
  • newSingleThreadExecutor: 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。
  • newWorkStealingPool(jdk1.8 新增):創(chuàng)建指定并行數(shù)的線程池,使用多重隊列減少競爭,不能保證線程執(zhí)行的順序。默認并行數(shù)為最大CPU內(nèi)核數(shù)

注意:線程池只是為了控制應用中處理某項業(yè)務中防止高并發(fā)問題帶來的線程不安全的發(fā)生的概率。線程不可以重用,每次新創(chuàng)建一個線程用來處理業(yè)務。可以通過線程池指定處理這項業(yè)務最大的同步線程數(shù)。

執(zhí)行任務流程

  1. 創(chuàng)建ExecutorService
    通過工具類java.util.concurrent.Executors的靜態(tài)方法來創(chuàng)建。
  2. 將任務添加到線程去執(zhí)行
    當將一個任務添加到線程池中的時候,線程池會為每個任務創(chuàng)建一個線程,該線程會在之后的某個時刻自動執(zhí)行。

二、幾種線程池詳解

線程示例

public class ExecutorsThread implements Runnable {
    
    private int index;

    public ExecutorsThread(int index) {
        this.index = index;
    }

    @Override
    public void run() {
        try {
            System.out.println("開始處理線程!");
            Thread.sleep(100 * index);
            System.out.println(String.format("我是線程%s[%s]", Thread.currentThread().getName(), this.toString()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

1、newCachedThreadPool
創(chuàng)建一個可緩存線程池,應用中存在的線程數(shù)最大是 231-1

@Test
public void testNewCachedThreadPool() throws InterruptedException {
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i=1; i<=10; i++) {
        ExecutorsThread executorsThread = new ExecutorsThread(i);
        executorService.execute(executorsThread);
    }
    Thread.sleep(1000*60);
}

2、newFixedThreadPool
創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。Executors.newFixedThreadPool(3);在線程池中保持三個線程可以同時執(zhí)行,但是注意,并不是說線程池中永遠都是這三個線程,只是說可以同時存在的線程數(shù),當某個線程執(zhí)行結(jié)束后,會有新的線程進來。
newFixedThreadPool.execute(new ThreadForpools());這句話的含義并不是添加新的線程,而是添加新的處理業(yè)務請求進來。當該線程獲取CPU時間后開始執(zhí)行。

@Test
public void testNewFixedThreadPool() throws InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int i=1; i<=10; i++) {
        ExecutorsThread executorsThread = new ExecutorsThread(i);
        executorService.execute(executorsThread);
    }
    Thread.sleep(1000*60);
}

3、newScheduledThreadPool
創(chuàng)建一個定長線程池,支持定時及周期性任務執(zhí)行。

@Test
public void testNewScheduledThreadPool() throws InterruptedException {
    ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
    for (int i=1; i<=10; i++) {
        ExecutorsThread executorsThread = new ExecutorsThread(i);
//      executorService.execute(executorsThread);
        executorService.schedule(executorsThread, 1, TimeUnit.SECONDS);
    }
    Thread.sleep(1000*60);
}

4、newSingleThreadExecutor
創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。

@Test
public void testNewSingleThreadExecutor() throws InterruptedException {
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    for (int i=1; i<=10; i++) {
        ExecutorsThread executorsThread = new ExecutorsThread(i);
        executorService.submit(executorsThread);
    }
    Thread.sleep(1000*60);
}

5、newWorkStealingPool
創(chuàng)建一個指定并行數(shù)的線程池,使用多重隊列減少競爭,不能保證線程執(zhí)行的順序。活動的線程數(shù)可以動態(tài)地增長或收縮。

@Test
public void testNewWorkStealingPool() throws InterruptedException {
    ExecutorService executorService = Executors.newWorkStealingPool(20);
    for (int i=1; i<=100; i++) {
        ExecutorsThread executorsThread = new ExecutorsThread(i);
        executorService.execute(executorsThread);
    }
    Thread.sleep(1000*60*2);
}

三、兩種任務

在Java5之后,任務分兩類
1、實現(xiàn) Runnable 接口的類
2、實現(xiàn) Callable 接口的類

  • 概述:返回結(jié)果并且可能拋出異常的任務。
  • 實現(xiàn)者定義了一個不帶任何參數(shù)的叫做 call 的方法。
  • Callable 接口類似于,兩者都是為那些其實例可能被另一個線程執(zhí)行的類設計的。但是 Runnable 不會返回結(jié)果,并且無法拋出經(jīng)過檢查的異常。
  • 類包含一些從其他普通形式轉(zhuǎn)換成 Callable 類的實用方法。
  • 任務執(zhí)行返回值Futrue<T> 可以收集返回值和執(zhí)行過程中遇到的異常信息,對于編寫高質(zhì)量的代碼很有幫助。還可以取消任務。

代碼實現(xiàn)

public class ExecutorsCallable implements Callable<String> {
    
    private int index;

    public ExecutorsCallable(int index) {
        this.index = index;
    }

    @Override
    public String call() throws Exception {
        System.out.println(String.format("任務[%s] call() 方法被自動調(diào)用!", Thread.currentThread().getName()));
        if (new Random().nextBoolean()) {
            throw new RuntimeException(String.format("任務[%s]執(zhí)行出錯!", Thread.currentThread().getName()));
        }
        for (int i=10000; i>0; i--);
        return String.format("任務[%s] call() 方法被自動調(diào)用,任務結(jié)果是%d!", Thread.currentThread().getName(), index);
    }
}
@Test
public void testCallable() throws InterruptedException {
    ExecutorService executorService = Executors.newCachedThreadPool();
    List<Future<String>> resultList = new ArrayList<>();
    
    for (int i=1; i<=10; i++) {
        ExecutorsCallable callable = new ExecutorsCallable(i);
        // ExecutorService 執(zhí)行 Callable 類型的任務,并將結(jié)果保存在 future 變量中
        Future<String> future = executorService.submit(callable);
        // 將執(zhí)行結(jié)果存儲到 list 中
        resultList.add(future);
    }

    //啟動一次順序關(guān)閉,執(zhí)行以前提交的任務,但不接受新任務。如果已經(jīng)關(guān)閉,則調(diào)用沒有其他作用。
    executorService.shutdown();
    
    for (Future<String> future : resultList) {
        try {
            System.out.println(future.get());
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    
    Thread.sleep(1000*60*2);
}
實現(xiàn) Runnable 接口的類與實現(xiàn) Callable 接口的類的區(qū)別

相同點

  • 都可以被ExecutorService執(zhí)行

不同點

  • Runnable 任務執(zhí)行 run 方法,而 Callable 執(zhí)行 call() 方法
  • Runnable 任務沒有返回值,而 Callable 任務有返回值
  • Runnable 任務不拋出異常,而 Callable 可以拋出檢查的異常
  • Callable 的 call() 方法只能通過 ExecutorService的 (<T> task) 方法來執(zhí)行,并且返回一個 <T><T>,是表示任務等待完成的 Future。
  • 將Runnable的對象傳遞給ExecutorService的submit方法,則該run方法自動在一個線程上執(zhí)行,并且會返回執(zhí)行結(jié)果Future對象,但是在該Future對象上調(diào)用get方法,將返回null。
    將一個Callable的對象傳遞給ExecutorService的submit方法,則該call方法自動在一個線程上執(zhí)行,并且會返回執(zhí)行結(jié)果Future對象。
submit與execute的區(qū)別
  • 接收的參數(shù)不一樣,submit 可以接收 Runnable 或 Callable 接口參數(shù),execute 只能接收 Runnable 接口參數(shù)。
  • submit 有返回值,execute 沒有。
  • submit 可以對 Exception 處理

結(jié)束語

1.清楚Runnable 和 Callable 任務的創(chuàng)建方式
2.ExecutorService 創(chuàng)建線程池的幾種方式與區(qū)別
3.ExecutorService submit 和 execute 的區(qū)別
4.Future<T> 的合理利用

參考資料
https://www.cnblogs.com/ljp-sun/p/6580147.html
https://www.cnblogs.com/wanqieddy/p/3853863.html

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

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