1. 程式人生 > >Java多執行緒系列--“JUC執行緒池”05之 執行緒池原理(四)

Java多執行緒系列--“JUC執行緒池”05之 執行緒池原理(四)

概要

本章介紹執行緒池的拒絕策略。內容包括:

  • 拒絕策略介紹
  • 拒絕策略對比和示例

拒絕策略介紹

執行緒池的拒絕策略,是指當任務新增到執行緒池中被拒絕,而採取的處理措施。 當任務新增到執行緒池中之所以被拒絕,可能是由於:第一,執行緒池異常關閉。第二,任務數量超過執行緒池的最大限制。

執行緒池共包括4種拒絕策略,它們分別是:AbortPolicy, CallerRunsPolicy, DiscardOldestPolicyDiscardPolicy

AbortPolicy         -- 當任務新增到執行緒池中被拒絕時,它將丟擲 RejectedExecutionException 異常。
CallerRunsPolicy    -- 當任務新增到執行緒池中被拒絕時,會線上程池當前正在執行的Thread執行緒池中處理被拒絕的任務。
DiscardOldestPolicy -- 當任務新增到執行緒池中被拒絕時,執行緒池會放棄等待佇列中最舊的未處理任務,然後將被拒絕的任務新增到等待佇列中。
DiscardPolicy       -- 當任務新增到執行緒池中被拒絕時,執行緒池將丟棄被拒絕的任務。

執行緒池預設的處理策略是AbortPolicy

拒絕策略對比和示例

下面通過示例,分別演示執行緒池的4種拒絕策略。

  1. DiscardPolicy 示例;
  2. DiscardOldestPolicy 示例;
  3. AbortPolicy 示例;
  4. CallerRunsPolicy 示例;
1、DiscardPolicy 示例
import java.lang.reflect.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.
util.concurrent.TimeUnit; import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy; public class DiscardPolicyDemo { private static final int THREADS_SIZE = 1; private static final int CAPACITY = 1; public static void main(String[] args) throws Exception { // 建立執行緒池。執行緒池的"最大池大小"和"核心池大小"都為1(THREADS_SIZE),"執行緒池"的阻塞佇列容量為1(CAPACITY)。
ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(CAPACITY)); // 設定執行緒池的拒絕策略為"丟棄" pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); // 新建10個任務,並將它們新增到執行緒池中。 for (int i = 0; i < 10; i++) { Runnable myrun = new MyRunnable("task-"+i); pool.execute(myrun); } // 關閉執行緒池 pool.shutdown(); } } class MyRunnable implements Runnable { private String name; public MyRunnable(String name) { this.name = name; } @Override public void run() { try { System.out.println(this.name + " is running."); Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } }

執行結果:

task-0 is running.
task-1 is running.

結果說明:

  • 執行緒池pool的"最大池大小"和"核心池大小"都為1(THREADS_SIZE),這意味著"執行緒池能同時執行的任務數量最大隻能是1"。
  • 執行緒池pool的阻塞佇列是ArrayBlockingQueue,ArrayBlockingQueue是一個有界的阻塞佇列,ArrayBlockingQueue的容量為1。這也意味著執行緒池的阻塞佇列只能有一個執行緒池阻塞等待。

根據""中分析的execute()程式碼可知:執行緒池中共運行了2個任務。第1個任務直接放到Worker中,通過執行緒去執行;第2個任務放到阻塞佇列中等待。其他的任務都被丟棄了!

2、DiscardOldestPolicy 示例
import java.lang.reflect.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;

public class DiscardOldestPolicyDemo {

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

    public static void main(String[] args) throws Exception {

        // 建立執行緒池。執行緒池的"最大池大小"和"核心池大小"都為1(THREADS_SIZE),"執行緒池"的阻塞佇列容量為1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY));
        // 設定執行緒池的拒絕策略為"DiscardOldestPolicy"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());

        // 新建10個任務,並將它們新增到執行緒池中。
        for (int i = 0; i < 10; i++) {
            Runnable myrun = new MyRunnable("task-"+i);
            pool.execute(myrun);
        }
        // 關閉執行緒池
        pool.shutdown();
    }
}

class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            System.out.println(this.name + " is running.");
            Thread.sleep(200);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

執行結果:

task-0 is running.
task-9 is running.

結果說明:將"執行緒池的拒絕策略"由DiscardPolicy修改為DiscardOldestPolicy之後,當有任務新增到執行緒池被拒絕時,執行緒池會丟棄阻塞佇列中末尾的任務,然後將被拒絕的任務新增到末尾。

3、AbortPolicy 示例
import java.lang.reflect.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.RejectedExecutionException;

public class AbortPolicyDemo {

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

    public static void main(String[] args) throws Exception {

        // 建立執行緒池。執行緒池的"最大池大小"和"核心池大小"都為1(THREADS_SIZE),"執行緒池"的阻塞佇列容量為1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY));
        // 設定執行緒池的拒絕策略為"丟擲異常"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());

        try {

            // 新建10個任務,並將它們新增到執行緒池中。
            for (int i = 0; i < 10; i++) {
                Runnable myrun = new MyRunnable("task-"+i);
                pool.execute(myrun);
            }
        } catch (RejectedExecutionException e) {
            e.printStackTrace();
            // 關閉執行緒池
            pool.shutdown();
        }
    }
}

class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            System.out.println(this.name + " is running.");
            Thread.sleep(200);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(某一次)執行結果:

java.util.concurrent.RejectedExecutionException
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1774)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:768)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:656)
    at AbortPolicyDemo.main(AbortPolicyDemo.java:27)
task-0 is running.
task-1 is running.

結果說明:將"執行緒池的拒絕策略"由DiscardPolicy修改為AbortPolicy之後,當有任務新增到執行緒池被拒絕時,會丟擲RejectedExecutionException。

4、CallerRunsPolicy 示例
import java.lang.reflect.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;

public class CallerRunsPolicyDemo {

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

    public static void main(String[] args) throws Exception {

        // 建立執行緒池。執行緒池的"最大池大小"和"核心池大小"都為1(THREADS_SIZE),"執行緒池"的阻塞佇列容量為1(CAPACITY)。
        ThreadPoolExecutor pool = new ThreadPoolExecutor(THREADS_SIZE, THREADS_SIZE, 0, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(CAPACITY));
        // 設定執行緒池的拒絕策略為"CallerRunsPolicy"
        pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 新建10個任務,並將它們新增到執行緒池中。
        for (int i = 0; i < 10; i++) {
            Runnable myrun = new MyRunnable("task-"+i);
            pool.execute(myrun);
        }

        // 關閉執行緒池
        pool.shutdown();
    }
}

class MyRunnable implements Runnable {
    private String name;
    public MyRunnable(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            System.out.println(this.name + " is running.");
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(某一次)執行結果:

task-2 is running.
task-3 is running.
task-4 is running.
task-5 is running.
task-6 is running.
task-7 is running.
task-8 is running.
task-9 is running.
task-0 is running.
task-1 is running.

結果說明:將"執行緒池的拒絕策略"由DiscardPolicy修改為CallerRunsPolicy之後,當有任務新增到執行緒池被拒絕時,執行緒池會將被拒絕的任務新增到"執行緒池正在執行的執行緒"中取執行。