【Java se】你不要过来啊之——线程池的拒绝策略

目录

什么是线程池

创建线程池

线程执行流程

常用的线程池

newFixedThreadPool——固定线程池

newCacheThreadPool——缓存线程池

newSingleThreadPool——单例线程池

newScheduledThreadPool——调度线程池

线程池的四种拒绝策略

AbortPolicy

DiscardPolicy

DiscardOlderPolicy

CallerRunsPolicy


什么是线程池

        是管理线程的池子,相比于手工创建、运行线程,使用线程池有如下优点:

  • 降低线程创建和销毁线程造成的系统开销
  • 提高响应速度。任务到达时,相对于手工创建一个线程,直接从线程池中拿线程,速度肯定快很多
  • 提高线程可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统稳定性,使用线程池可以进行统一分配、调控和监控

创建线程池

        线程池可以通过ThreadPoolExecutor来创建

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,             
                          TimeUnit unit, BlockingQueue workQueue, 
                          ThreadFactory threadFactory, RejectedExecutionHandler handler)

        核心参数的作用:

corePoolSize 线程池核心线程数最大值
maximumPoolSize 线程池最大线程数大小
keepAliveTime 线程池中非核心线程空闲的存活时间大小
unit 线程空闲存货时间单位
workQueue 存放任务的阻塞队列
threadFactory 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题
handler 线程池的饱和策略事件(拒绝策略)

 

线程执行流程

【Java se】你不要过来啊之——线程池的拒绝策略_第1张图片

 

常用的线程池

  • newFixedThreadPool——固定线程池

        固定数目线程的线程池。底层使用LinkedBlockingQueue阻塞队列。

  • 前两个参数相同,通过传参设置
  • 可通过向上转型(ThreadPoolExecutor)set方法更改参数

  • newCacheThreadPool——缓存线程池

        可缓存线程的线程池。底层使用SynchronousQueue阻塞队列。

  • 核心线程数为0,最大线程池数为Integer_MAX
  • 来一个创建一个
  • 队列不堆积任务
  • 擅长执行多个短期异步任务

  • newSingleThreadPool——单例线程池

        单线程的线程池。底层使用LinkedBlockingQueue阻塞队列。

  • 不能重新配置,不能修改核心线程数量
  • 线程中只有1个线程,前两个参数都为1
  • 不能通过向上转型修改参数

  • newScheduledThreadPool——调度线程池

        定时及周期执行的线程池。底层使用DelayedWorkQueue延迟工作队列。

  • 核心线程数通过传入参数设置,最大线程池数为Integer_MAX
  • 有三种schedule方法,有计时器效果

线程池的四种拒绝策略

        创建线程任务,一个线程任务执行一秒:

class TaskThread implements Runnable{

        private int i;

        public TaskThread(int i) {
            this.i = i;
        }

        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println("执行任务:" + i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
  • AbortPolicy

        默认策略,丢弃任务并派出RejectedExecutionException异常。

// 拒绝策略 默认拒绝策略,拒绝任务并抛出异常:
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler);
        for (int i = 1; i <= 5; i++) {
            try {
                threadPool.execute(new TaskThread(i));
            } catch (Exception e) {
                System.out.println("【任务" + i + "】报错:" + e.getMessage());
            }

        }

        输出结果:

【任务】4报错:Task com.test.controller.ThreadPoolController$TaskThread@5c0369c4 rejected from java.util.concurrent.ThreadPoolExecutor@50675690[Running, pool size = 2, active threads = 2, queued tasks = 1, completed tasks = 0]
【任务】5报错:Task com.test.controller.ThreadPoolController$TaskThread@31b7dea0 rejected from java.util.concurrent.ThreadPoolExecutor@50675690[Running, pool size = 2, active threads = 2, queued tasks = 1, completed tasks = 0]
执行任务:1
执行任务:3
执行任务:2

        最大线程数 + 阻塞队列 = 3,执行到4,5的时候就抛出错误。这里需要用 try catch 捕获异常。任务1、2、3正常执行。

如果提交的任务都要执行,可以将抛出的错误任务存入在redis中,然后定时从redis中获取任务,再提交执行。

 

  • DiscardPolicy

【Java se】你不要过来啊之——线程池的拒绝策略_第2张图片 

        丢弃任务,不抛出异常。

        更改代码中的参数,运行输出:

执行任务:1
执行任务:3
执行任务:2

        多余的线程任务提交被拒绝,而只执行最大线程数 + 阻塞队列 数量的任务,并且不会抛出错误。

  • DiscardOlderPolicy

        丢弃队列中的末尾任务(最旧的任务,也是最早入队列的任务)后,继续将当前任务提交给线程池。
        更改代码中的参数,运行输出:

执行任务:3
执行任务:1
执行任务:5

        任务的执行顺序是 核心线程数 —> 阻塞队列 —> 最大线程数,其中任务1,任务3提交成功。

  • 任务2因为在阻塞队列中,
  • 后面的任务4把任务2挤掉,
  • 任务5又把任务4挤掉,所以最后执行的是任务5。

  • CallerRunsPolicy

        交给调用线程池的线程进行处理(谁调用,谁处理)。不进入线程池执行,在这种方式(CallerRunsPolicy)中,任务将由调用者线程去执行。(用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。

        更改代码中的参数,运行输出:

执行任务:1
执行任务:4
执行任务:3
执行任务:2
执行任务:5

        最大线程数 + 阻塞队列 = 3,多余的任务还是继续被执行。 

你可能感兴趣的:(java,开发语言)