Java 線程池之 四個拒絕策略

jdk中關於線程池的拒絕策略的接口的實現,暫時就四個,如下:

AbortPolicy         -- 當任務添加到線程池中被拒絕時,它將拋出 RejectedExecutionException 異常。
CallerRunsPolicy    -- 當任務添加到線程池中被拒絕時,會在線程池當前正在運行的Thread線程池中處理被拒絕的任務。
DiscardOldestPolicy -- 當任務添加到線程池中被拒絕時,線程池會放棄等待隊列中最舊的未處理任務,然後將被拒絕的任務添加到等待隊列中。
DiscardPolicy       -- 當任務添加到線程池中被拒絕時,線程池將丟棄被拒絕的任務。

真的是4個實現,圖裏面剩下的幾個是其它jar裏面的實現,可以不管。

只有第一個會拋異常,這個還是默認的拒絕策略,其它3個都是默默的消化掉問題。

當任務添加到線程池中之所以被拒絕,可能是由於:第一,線程池異常關閉。第二,任務數量超過線程池的最大限制。

測試,使用的runnable代碼

package com.lxk.thread.threadpool.executors.reject;

/**
 * @author LiXuekai on 2020/3/27
 */
public class MyRunnable implements Runnable {
    private String name;

    public MyRunnable(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        try {
            String name = "當前線程名稱:" + Thread.currentThread().getName();
            System.out.println(name + ", " + this.name + " is running.");
            Thread.sleep(200);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

下面用代碼記錄一下幾個策略的使用場景。

1,AbortPolicy

package com.lxk.thread.threadpool.executors.reject;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.*;

/**
 * AbortPolicy 當任務添加到線程池中被拒絕時,它將拋出 RejectedExecutionException 異常。
 * <p>
 * 結果說明:
 * 將"線程池的拒絕策略"由DiscardPolicy修改爲AbortPolicy之後,當有任務添加到線程池被拒絕時,會拋出RejectedExecutionException。
 *
 * @author LiXuekai on 2020/3/27
 */
public class TestAbortPolicy {

    private static final int THREADS_SIZE = 1;
    private static final int TASK_MAX = 10;
    private static final int CAPACITY = 1;

    public static void main(String[] args) throws Exception {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("Test-Rejected-Policy-Pool-%d").build();

        // 創建線程池。線程池的"最大池大小"和"核心池大小"都爲1(THREADS_SIZE),"線程池"的阻塞隊列容量爲1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());

        // 新建10個任務,並將它們添加到線程池中。
        for (int i = 0; i < TASK_MAX; i++) {
            Runnable myRunnable = new MyRunnable("task [ " + i + " ]");
            try {
                pool.execute(myRunnable);
            } catch (RejectedExecutionException e) {
                System.out.println(e.toString());
            }
        }

        // 關閉線程池
        pool.shutdown();

    }
}

運行結果:

只有0,1兩個任務運行OK,其它的8個任務,在往線程池丟的時候,被線程池拒絕了,而且還拋了異常,被catch住了,catch了8次。

2,CallerRunsPolicy

package com.lxk.thread.threadpool.executors.reject;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * CallerRunsPolicy 當任務添加到線程池中被拒絕時,會在線程池當前正在運行的Thread線程池中處理被拒絕的任務
 * 翻譯一下:就是不進入線程池執行,在這種方式(CallerRunsPolicy)中,任務將有調用者線程去執行
 *
 * @author LiXuekai on 2020/3/27
 */
public class TestCallerRunsPolicy {

    private static final int THREADS_SIZE = 1;
    private static final int TASK_MAX = 10;
    private static final int CAPACITY = 1;

    public static void main(String[] args) throws Exception {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("Test-Rejected-Policy-Pool-%d").build();

        // 創建線程池。線程池的"最大池大小"和"核心池大小"都爲1(THREADS_SIZE),"線程池"的阻塞隊列容量爲1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY), namedThreadFactory);
        // 設置線程池的拒絕策略爲"CallerRunsPolicy"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 新建10個任務,並將它們添加到線程池中。
        for (int i = 0; i < TASK_MAX; i++) {
            Runnable myRunnable = new MyRunnable("task [ " + i + " ]");
            try {
                pool.execute(myRunnable);
            } catch (Exception e) {
                System.out.println(e.toString());
            }
        }

        // 關閉線程池
        pool.shutdown();
    }
}

運行結果圖:

在任務往線程池丟的時候,發現線程池已經裝不下了,那麼這個時候,就讓往線程池丟任務丟這個線程來執行這個任務,在此例子就是main線程了,從結果圖可見線程池裏面的一個線程和main一起幹活,把10個任務給搞完了。

3,DiscardOldestPolicy

package com.lxk.thread.threadpool.executors.reject;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 當任務添加到線程池中被拒絕時,線程池會放棄等待隊列中最舊的未處理任務,然後將被拒絕的任務添加到等待隊列中。
 * <p>
 * 結果說明:
 * 將"線程池的拒絕策略"由DiscardPolicy修改爲DiscardOldestPolicy之後,當有任務添加到線程池被拒絕時,
 * 線程池會丟棄阻塞隊列中末尾的任務,然後將被拒絕的任務添加到末尾。
 *
 * @author LiXuekai on 2020/3/27
 */
public class TestDiscardOldestPolicy {

    private static final int THREADS_SIZE = 1;
    private static final int TASK_MAX = 10;
    private static final int CAPACITY = 1;

    public static void main(String[] args) throws Exception {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("Test-Rejected-Policy-Pool-%d").build();

        // 創建線程池。線程池的"最大池大小"和"核心池大小"都爲1(THREADS_SIZE),"線程池"的阻塞隊列容量爲1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY), namedThreadFactory);
        // 設置線程池的拒絕策略爲"DiscardOldestPolicy"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());

        // 新建10個任務,並將它們添加到線程池中。
        for (int i = 0; i < TASK_MAX; i++) {
            Runnable myRunnable = new MyRunnable("task [ " + i + " ]");
            try {
                pool.execute(myRunnable);
            } catch (Exception e) {
                System.out.println(e.toString());
            }
        }
        // 關閉線程池
        pool.shutdown();
    }
}

運行結果:

可見0任務到池子之後,運行,剩下1-9在來池子的時候,沒位置了,都的排隊,但位置就1個,那每次新來的都會不客氣但把舊時代的給擠掉。也就是這個策略的名字的由來。

4,DiscardPolicy

package com.lxk.thread.threadpool.executors.reject;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * DiscardOldestPolicy -- 當任務添加到線程池中被拒絕時,線程池會放棄等待隊列中最舊的未處理任務,然後將被拒絕的任務添加到等待隊列中。
 * 結果說明:線程池pool的"最大池大小"和"核心池大小"都爲1(THREADS_SIZE),這意味着"線程池能同時運行的任務數量最大隻能是1"。
 * 線程池pool的阻塞隊列是ArrayBlockingQueue,ArrayBlockingQueue是一個有界的阻塞隊列,ArrayBlockingQueue的容量爲1。這也意味着線程池的阻塞隊列只能有一個線程池阻塞等待。
 * 根據""中分析的execute()代碼可知:線程池中共運行了2個任務。第1個任務直接放到Worker中,通過線程去執行;第2個任務放到阻塞隊列中等待。其他的任務都被丟棄了!
 *
 * @author LiXuekai on 2020/3/27
 */
public class TestDiscardPolicy {

    private static final int THREADS_SIZE = 1;
    private static final int TASK_MAX = 10;
    private static final int CAPACITY = 1;

    public static void main(String[] args) throws Exception {
        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("Test-Rejected-Policy-Pool-%d").build();

        // 創建線程池。線程池的"最大池大小"和"核心池大小"都爲1(THREADS_SIZE),"線程池"的阻塞隊列容量爲1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY), namedThreadFactory);
        // 設置線程池的拒絕策略爲"丟棄"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

        // 新建10個任務,並將它們添加到線程池中。
        for (int i = 0; i < TASK_MAX; i++) {
            Runnable myRunnable = new MyRunnable("task [ " + i + " ]");
            try {
                pool.execute(myRunnable);
            } catch (Exception e) {
                System.out.println(e.toString());
            }
        }
        // 關閉線程池
        pool.shutdown();
    }
}

運行結果:

任務0來了池子,先搶佔了線程,可以執行,之後來的都的在隊列裏排隊,但隊列就一個位置,先來的佔着位置,後面的來只能看着,被無情的拋棄,所以,輸出結果就0,1兩個任務執行,其它的都消失了。

 

最後:

1,幾個代碼都是創建了一個 "最大池大小"和"核心池大小"都爲1 的線程池,意味着"線程池能同時運行的任務數量最大隻能是1"

2,創建線程池的時候,應該使用 ThreadPoolExecutor 這個構造函數,可以牢記複習線程池創建原理,另外還的使用帶factory參數的構造函數,可以設置池子中的線程的名稱,出問題的時候,也好排查。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章