线程池的四种饱和策略

2,013 阅读3分钟

当一个线程池接收任务等到工作队列满了,并且最大线程数也满了,这时候再来任务的话线程池就会执行饱和策略了,有四种饱和策略分别是:Abort 策略, CallerRuns 策略,Discard策略,DiscardOlds策略

1,Abort 策略 (丢弃任务并抛出RejectedExecutionException异常)

详解:线程池里同时来了8个任务,首先会创建两个核心线程,然后往等待队列中放5个任务,剩下还有3个任务,
这时候因为线程池最大线程数量是3,除去核心线程数还剩1个线程名额,这时候就会创建一个线程来执行一个任务,最后剩两个任务需要执行饱和策略AbortPolicy,

ExecutorService exec = new ThreadPoolExecutor(2, 3, 0L, TimeUnit.SECONDS,
        new LinkedBlockingDeque<Runnable>(5), new ThreadPoolExecutor.AbortPolicy());
private void putrunnable() {
    for (int i = 0; i < 10; i++) {
        exec.submit(new Task());
    }
    exec.shutdown();
}

static class Task implements Runnable {
    private static int count = 0;
    private int id = 0;
    public Task() {
        id = ++count;
    }
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("任务" + id + ":" + Thread.currentThread().getName());
    }
}

public static void main(String[] args) {
    new Test().putrunnable();
}

下面是控制台输出:(只有8个任务被执行了)
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@135fbaa4 
    rejected from java.util.concurrent.ThreadPoolExecutor@45ee12a7[Running, pool size = 3, active threads = 3, queued tasks = 5, 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 java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
	at com.renai.cn.admin.Test.putrunnable(Test.java:14)
	at com.renai.cn.admin.Test.main(Test.java:37)
任务2:pool-1-thread-2
任务1:pool-1-thread-1
任务8:pool-1-thread-3
任务3:pool-1-thread-1
任务4:pool-1-thread-2
任务5:pool-1-thread-3
任务6:pool-1-thread-1
任务7:pool-1-thread-2

2,DiscardPolicy策略 (丢弃任务,但是不抛出异常)

代码同上,只是改成了 new ThreadPoolExecutor.DiscardPolicy()

控制台输出:(无异常)
任务8:pool-1-thread-3
任务2:pool-1-thread-2
任务1:pool-1-thread-1
任务4:pool-1-thread-2
任务3:pool-1-thread-3
任务5:pool-1-thread-1
任务6:pool-1-thread-2
任务7:pool-1-thread-3

3,DiscardOldestPolicy策略 (丢弃队列最前面的任务,然后重新提交被拒绝的任务)

 此拒绝策略,是一种喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量。
 功能:如果线程池未关闭,就弹出队列头部的元素,然后尝试执行
 使用场景:这个策略还是会丢弃任务,丢弃时也是毫无声息,但是特点是丢弃的是老的未执行的任务,而且是待执行优先级较高的任务。
     基于这个特性,想到的场景就是,发布消息和修改消息,当消息发布出去后,还未执行,此时更新的消息又来了,
     这个时候未执行的消息的版本比现在提交的消息版本要低就可以被丢弃了。因为队列中还有可能存在消息版本更低的消息会排队执行,
     所以在真正处理消息的时候一定要做好消息的版本比较。 
     
代码同上,只是改成了 new ThreadPoolExecutor.DiscardOldestPolicy()
控制台输出:
任务2:pool-1-thread-2
任务1:pool-1-thread-1
任务8:pool-1-thread-3
任务5:pool-1-thread-1
任务6:pool-1-thread-2
任务9:pool-1-thread-1
任务7:pool-1-thread-3
任务10:pool-1-thread-2

(一共运行8个任务,程序结束,后面添加的任务9,任务10被执行了,而前面的任务3,任务4被丢弃。)

4,CallerRuns策略 (由调用线程处理该任务)

由调用的主线程来执行任务
代码同上,只是改了 new ThreadPoolExecutor.CallerRunsPolicy()
所有的任务都被运行,且有2(任务910)个任务是在main线程中执行成功的,8个任务在线程池中的线程执行的。
也有可能main线程只执行一个,因为可能出现最后一个加入线程池的时候,第一个已经执行完毕。

控制台输出:
任务9:main
任务2:pool-1-thread-2
任务8:pool-1-thread-3
任务1:pool-1-thread-1
任务10:main
任务3:pool-1-thread-2
任务4:pool-1-thread-3
任务5:pool-1-thread-1
任务6:pool-1-thread-2
任务7:pool-1-thread-3