线程池--拒绝策略RejectedExecutionHandler 原理和实验

 

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 默认策略

ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程) 

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

下面来看几个例子:

1. AbortPolicy策略:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.RejectedExecutionException;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

 

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();

        

        }

}

输出:

task-0 is running.

java.util.concurrent.RejectedExecutionException: Task MyRunnable@68de145 rejected from java.util.concurrent.ThreadPoolExecutor@27fa135a[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]

at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)

at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)

at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)

at AbortPolicyDemo.main(AbortPolicyDemo.java:18)

task-1 is running.

结果分析:

先是task0占有了唯一的线程执行额度并开始执行,随后task1占有阻塞队列的唯一空间,随后task2想执行,发现task0还没跑完,执行额度又满了,于是尝试进入阻塞队列,结果发现阻塞队列被task1给占了,无路可走的task2于是抛出异常,关闭线程池。

 

2.DiscardPolicy策略:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

 

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();

        }

    }

}

 

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<>(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();

    }

}

输出结果:

task-0 is running.

task-1 is running.

结果分析:

线程池的第一个参数决定可以并发几个线程,这里设为1,那么就表示线程只能一个一个来,另一个参数是最后这个ArrayBlockingQueue所代表的,意思是如果新提交到线程池的线程如果当前没有执行额度(例如上面这种一次只能执行1个线程,我这里就暂称为执行额度为1),那么只能放到这个阻塞队列中等待,而阻塞队列本身也是有大小的,所以也会满,满了怎么办,就对应上面说的四种策略,所以上面的代码如果在33行后加一行Thread.sleep(200);则10个线程都能得到执行,因为上面设定了每个线程的执行时间为100毫秒,那么只要等超过100毫秒的时间,线程就会执行完毕并释放资源,则后序提交的线程就来得及进入线程池,否则像上面的代码那样太急着提交,直接就会导致阻塞的发生,从而触发拒绝策略。

 

3. DiscardOldestPolicy策略:

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

 

 

 

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(); }

}

输出结果为:

task-0 is running.

 

task-9 is running.

结果分析:

可见1到8都被挤掉了,推演如下:先task0进入开始执行200ms,在这200ms内,发生了如下后序事件:task1进入阻塞队列等待,task2想进入阻塞队列,但是发现task1占着茅坑,于是把它挤出去,取而代之,task3想进入阻塞队列,又发现task2占着茅坑,于是把它挤出去,取而代之,以此类推,最终task-9占据了阻塞队列的位置,并等task0完成后task9开始执行。于是乎有了上面的输出。

 

4. CallerRunsPolicy

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

 

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<>(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();

    }

}

结果:

task-2 is running.

task-0 is running.

task-3 is running.

task-1 is running.

task-5 is running.

task-4 is running.

task-7 is running.

task-6 is running.

task-9 is running.

 

task-8 is running.

结果分析:

首先,所有的人物都得到了执行,没有一个被漏掉或者抛出异常什么的,其次,注意到顺序看起来乱七八糟,但是仔细看还是乱中有序,可以这样理解:相当于现在有三个槽,一个是执行线程空间,一个是阻塞队列,一个是主线程,前两个槽视为一个整体,这个整体跟主线程的槽对比,谁有空,线程就进入谁,最终可以决定执行的顺序。

猜你喜欢

转载自blog.csdn.net/xunileida/article/details/83956897