回覆列表
  • 1 # 碼農的一天

    ThreadPoolExecutor 的類關係如下:

    Executor是一個介面,它是Executor框架的基礎,它將任務的提交與任務的執行分離開來。

    ExecutorService介面繼承了Executor,在其上做了一些shutdown()、submit()的擴充套件,可以說是真正的執行緒池介面;

    AbstractExecutorService抽象類實現了ExecutorService介面中的大部分方法;

    threadPoolExecutor是執行緒池的核心實現類,用來執行被提交的任務。

    ScheduledExecutorService介面繼承了ExecutorService介面,提供了帶"週期執行"功能ExecutorService;

    ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲後執行命令,或者定期執行命令。ScheduledThreadPoolExecutor比Timer更靈活,功能更強大。

    定義執行緒池

    執行緒池的建立各個引數含義 及流程 如下:

    1、corePoolSize

    ① 執行緒池中的核心執行緒數,當提交一個任務時,執行緒池建立一個新執行緒執行任務,直到當前執行緒數等於corePoolSize;

    ② 如果當前執行緒數為corePoolSize,繼續提交的任務被儲存到阻塞佇列中,等待被執行;

    2、maximumPoolSize

    執行緒池中允許的最大執行緒數。如果當前阻塞佇列滿了,且繼續提交任務,

    則建立新的執行緒執行任務,前提是當前執行緒數小於maximumPoolSize

    3、keepAliveTime

    執行緒空閒的存活時間,即當執行緒沒有任務執行時,繼續存活的時間。

    預設情況下,該引數只在執行緒數大於 corePoolSize時才有用。

    4、TimeUnit

    keepAliveTime 的時間單位。

    5、WorkQueue

    用於儲存等待執行的任務的阻塞佇列,一般來說,我們應該儘量使用有界佇列,因為使用無界佇列作為工作佇列會對執行緒池做如下影響。

    ① 當執行緒池中的執行緒數達到corePoolSize後,新任務將在無界佇列中等待,因此執行緒池中的執行緒數不會超過corePoolSize。

    ② 由於1,使用無界佇列時maximumPoolSize將是一個無效引數。

    ④ 更重要的,使用無界queue可能會耗盡系統資源,有界佇列則有助於防止資源耗盡,同時即使使用有界佇列,也要儘量控制佇列的大小在一個合適的範圍。

    所以我們一般會使用,ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、PriorityBlockingQueue。

    6、threadfactory

    建立執行緒的工廠,透過自定義的執行緒工廠可以給每個新建的執行緒設定一個具有識別度的執行緒名,

    當然還可以更加自由的對執行緒做更多的設定,比如設定所有的執行緒為守護執行緒。

    Executors 靜態工廠裡預設的threadfactory,執行緒的命令 規則時"pool-數字-thread-數字"

    7、RejectedExecutionHandler 拒絕策略

    執行緒池的飽和策略,當阻塞佇列滿了,且沒有空閒的工作執行緒,如果繼續提交任務,必須採取一種策略處理該任務,執行緒池提供了4種策略:

    ① AbortPolicy : 直接丟擲異常,預設策略

    ② CallerRunsPolicy: 用呼叫者所在的執行緒來執行任務

    ④ Discardpolicy : 直接丟棄任務

    當然也可以根據應用場景實現RejectedExecutionHandler介面,自定義飽和策略,如記錄日誌或持久化儲存不能處理的任務。

    執行緒池的工作機制

    ① 如果當前執行的執行緒 少於corePoolSize,則建立新執行緒來執行任務

    ② 如果執行的執行緒等於或多於corePoolSize,則將任務加入BlockingQueue

    ④ 如果建立新執行緒將使當前執行的執行緒超出 maximumPoolSize,任務將被拒絕,

    並呼叫RejectedExecutionHandler.rejectedExecution()方法。

  • 2 # 小光童鞋

    ThreadPool工作原理任務先去核心執行緒池,如果核心執行緒池沒有則存入到Q中;Q滿了去啟新的執行緒(啟新執行緒時會全域性鎖),不能啟的話就返回錯誤需要多瞭解些,引數配置和使用結合。

  • 3 # Java從入門到架構師

    執行緒池如何工作?

    執行緒池把先前建立的執行緒重用於當前任務。這就解決了需要太多執行緒的問題,因此記憶體不足不是一個選擇。您甚至可以把執行緒池視為回收系統。它不止消除了用盡記憶體的選項,而且還使應用程式非常快速地響應,原因是當請求到達時已經存在一個執行緒。

    上圖的工作流程允許您控制應用程式正在建立的執行緒數,還可以控制計劃任務的執行並把傳入的任務保持在佇列中。

    執行者,執行者和執行者服務

    Java提供了Executor框架,這意味著您只需要實現Runnable物件並把其傳送給executor即可執行。

    不同的執行器執行緒池方法

    newFixedThreadPool(int size) - creates a fixed size thread pool

    newCachedThreadPool() - creates a thread pool that creates new threads if needed but will also use previous threads if they are available

    newSingleThreadExecutor() - creates a single thread

    ExecutorService介面包含許多方法。您可以使用Future例項控制任務的執行。有關如何使用Future的示例:

    ExecutorService execService = Executors.newFixedThreadPool(6);

    Future<String> future = execService.submit(() -> "Example");

    String result = future.get();

    ThreadPoolExecutor 使您可以實現具有許多引數的可擴充套件執行緒池,這些引數包括corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,handler,threadFactor。但是,corePoolSize,maximumPoolSize和keepAliveTime是主要變數,原因是它們在每個建構函式中都使用。

    maximumPoolSize是池中允許的最大執行緒數。

    有關其他引數的更多資訊,請訪問原始Oracle文件。

    執行緒池實現示例

    工作流程步驟:

    建立要執行的任務

    使用執行程式建立執行程式池

    把任務傳遞給執行程式池

    關閉執行程式池

    Task.java

    import java.text.SimpleDateFormat;

    import java.util.Date;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    // (Step 1)

    public class Task implements Runnable {

    private String name;

    public Task(String name) {

    this.name = name;

    }

    public void run() {

    try {

    for (int i = 0; i < 5; i++) {

    if (i == 1) {

    Date date = new Date();

    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");

    System.out.println("Time initialization for task " + this.name + " is " + ft.format(date));

    }

    else {

    Date date = new Date();

    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");

    System.out.println("Execution time for task " + this.name + " is " + ft.format(date));

    }

    Thread.sleep(1000);

    }

    }

    catch(InterruptedException error) {

    error.printStackTrace();

    }

    System.out.println(this.name + " completed");

    }

    }

    Main.java

    import java.text.SimpleDateFormat;

    import java.util.Date;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    public class Main {

    public static void main(String[] args) {

    Runnable task1 = new Task("task 1");

    Runnable task2 = new Task("task 2");

    Runnable task3 = new Task("task 3");

    Runnable task4 = new Task("task 4");

    Runnable task5 = new Task("task 5");

    // (Step 2)

    ExecutorService pool = Executors.newFixedThreadPool(3);

    // (Step 3)

    pool.execute(task1);

    pool.execute(task2);

    pool.execute(task3);

    pool.execute(task4);

    pool.execute(task5);

    // (Step 4)

    pool.shutdown();

    }

    }

    輸出:

    Time initialization for task task 2 is 10:18:40

    Time initialization for task task 1 is 10:18:40

    Time initialization for task task 3 is 10:18:40

    Execution time for task task 3 is 10:18:41

    Execution time for task task 1 is 10:18:41

    Execution time for task task 2 is 10:18:41

    Execution time for task task 2 is 10:18:42

    Execution time for task task 3 is 10:18:42

    Execution time for task task 1 is 10:18:42

    Execution time for task task 1 is 10:18:43

    Execution time for task task 3 is 10:18:43

    Execution time for task task 2 is 10:18:43

    Execution time for task task 3 is 10:18:44

    Execution time for task task 1 is 10:18:44

    Execution time for task task 2 is 10:18:44

    task 2 completed

    task 1 completed

    task 3 completed

    Time initialization for task task 4 is 10:18:45

    Time initialization for task task 5 is 10:18:45

    Execution time for task task 4 is 10:18:46

    Execution time for task task 5 is 10:18:46

    Execution time for task task 4 is 10:18:47

    Execution time for task task 5 is 10:18:47

    Execution time for task task 5 is 10:18:48

    Execution time for task task 4 is 10:18:48

    Execution time for task task 4 is 10:18:49

    Execution time for task task 5 is 10:18:49

    task 4 completed

    task 5 completed

    上面的程式碼實現的細分:

    Task.java表示任務類。每個任務都有一個名稱例項變數,並且每個任務都使用建構函式例項化。此類有1個方法,稱為執行。在run 方法的主體內 ,有一個for迴圈,該迴圈根據存在的任務數進行迭代。在我們的例子中,有5個任務,這意味著它把執行5次。第一次迭代,顯示當前任務初始化的時間。其他迭代,列印執行時間。列印完成後,有一個Thread.sleep()方法呼叫,該方法用於以1秒的延遲顯示每個迭代訊息。 注意 ,方法名稱“ run” 很重要,原因是它是來自Runnable 的抽象方法 我們的Task類正在實現。

    僅在池中的某個胎面變得空閒時才執行任務4和5。在此之前,把多餘的任務放在 佇列中。

    執行完所有任務後,請關閉執行緒池。

    執行緒池何時有用

    組織伺服器應用程式時。如本文開頭所述,在組織伺服器應用程式時非常有用,原因是使用執行緒池非常有效,就像有許多工一樣,它會自動把它們放入佇列中。不止如此,它還可以防止記憶體不足,或者至少可以顯著減慢這樣做的速度。使用ExecutorService使其更易於實現。

  • 4 # 樂淘學程式設計

    通俗易懂的給你解釋一下,池塘裡有很多魚,我們撈一條上來把玩一番,把玩一段時間後放回池塘裡去。這就是執行緒池的工作原理,只不過這個池塘裡放的不是魚,是一個個執行緒物件,用到一個就拿一個,用完放回池塘內,而這個池塘在計算機內就是對應在軟體的記憶體中了。

  • 5 # Java技術棧

    java.util.concurrent包中我們能找到執行緒池的定義,其中ThreadPoolExecutor是我們執行緒池核心類,首先看看執行緒池類的主要引數有哪些。

    corePoolSize:執行緒池的核心大小,也可以理解為最小的執行緒池大小。

    maximumPoolSize:最大執行緒池大小。

    keepAliveTime:空餘執行緒存活時間,指的是超過corePoolSize的空餘執行緒達到多長時間才進行銷燬。

    unit:銷燬時間單位。

    workQueue:儲存等待執行執行緒的工作佇列。

    threadFactory:建立執行緒的工廠,一般用預設即可。

    handler:拒絕策略,當工作佇列、執行緒池全已滿時如何拒絕新任務,預設丟擲異常。

    執行緒池工作流程

    1、如果執行緒池中的執行緒小於corePoolSize時就會建立新執行緒直接執行任務。

    2、如果執行緒池中的執行緒大於corePoolSize時就會暫時把任務儲存到工作佇列workQueue中等待執行。

    3、如果工作佇列workQueue也滿時:當執行緒數小於最大執行緒池數maximumPoolSize時就會建立新執行緒來處理,而執行緒數大於等於最大執行緒池數maximumPoolSize時就會執行拒絕策略。

  • 中秋節和大豐收的關聯?
  • 有人說“有膽量、有野心,才能出人頭地”。你認同嗎?你怎麼看?