ThreadPoolExecutor执行原理

线程池

基本概念: 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。

不要使用Executor创建线程池,而是通过ThreadPoolExecutor的方式创建,这样的处理方式能让编写代码的人更加明确线程池的运行规则,规避资源耗尽的风险。

说明:Executors 返回的线程池对象的弊端如下:

1)FixedThreadPool和SingleThreadPool:

允许请求的队列长度为Interger.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

2)CachedThreadPool:

允许的创建线程数量为:Interger.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

Executor创建线程的3大方法:

//Executor不建议使用,容易引发OOM
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单线程
ExecutorService threadPool1 = Executors.newFixedThreadPool(5);//固定线程
ExecutorService threadPool2 = Executors.newCachedThreadPool();//可变线程

for (int i = 0; i < 10; i++) {
    threadPool2.execute(()->{
        System.out.println(Thread.currentThread().getName());
    });
}

ThreadPoolExecutor的七大参数:

核心线程数,最大线程数,超时等待时间,超时等待单位,阻塞队列,线程工厂,拒绝策略。

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

执行过程:如果线程数没有超过核心线程数,那么就会用到几个开辟几个,如果大于核心线程数,但是没有超过阻塞队列的个数,那么就会等待核心线程执行完,再取阻塞队列中的执行

测试代码

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
    2,
    6,
    3,
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(3),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()
);
try {
    for (int i = 0; i < 7; i++) {
        final int temp = i;
        threadPoolExecutor.execute(()->
                                   {
                                       try {
                                           System.out.println(Thread.currentThread().getName()+" "+temp);
                                           TimeUnit.SECONDS.sleep(1);
                                       } catch (InterruptedException e) {
                                           e.printStackTrace();
                                       }

                                   });
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    threadPoolExecutor.shutdown();
}

调整不同的循环数会出现不同的执行顺序,甚至会报错,具体是什么原理呢?下面用画图的方式描述出来。

执行过程

以核心线程数为2,最大线程数为6,阻塞队列容量为3 为例:

  • 如果要执行的任务数小于等于核心线程数:

在这里插入图片描述
如图,蓝色为待执行的任务,黄色为核心线程,当要执行的任务数小于等于核心线程数时,直接被核心线程接管:

在这里插入图片描述

  • 当待执行的任务数大于核心线程数,但是小于核心线程数+阻塞队列容量时:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
task1,task2被核心线程接管,task3进阻塞队列等待核心线程执行完毕再执行。

  • 当待执行的任务数大于核心线程数+阻塞队列容量但小于最大线程数+阻塞队列容量时:

在这里插入图片描述

因为阻塞队列已达到最大容量,剩余的线程(除去核心线程的其他线程)会被启动,用来直接接管新来的任务,当线程经过keepAliveTime还没有接到任务,则再次关闭线程。

注意:直接接管新来的任务,而不是从阻塞队列中取

  • 当待执行的任务数大于了最大线程数+阻塞队列容量时:

在这里插入图片描述
在这里插入图片描述

超出数量的任务会被拒绝,具体的拒绝策略有四种:

  • ThreadPoolExecutor.AbortPolicy():该策略是指,当有超出数量的任务来时,会抛出异常

在这里插入图片描述

  • ThreadPoolExecutor.CallerRunsPolicy();:超出数量的任务会被调用者线程执行,比如:

在这里插入图片描述
因为是主线程调用的,所以会被主线程接管。

  • ThreadPoolExecutor.DiscardPolicy();:当超出数量的任务来时,不会抛出异常

在这里插入图片描述
​ 可以看出当循环次数为10时,只执行了9个,多出的任务被丢弃。

  • ThreadPoolExecutor.DiscardOldestPolicy();:超出数量的任务会尝试去和最早执行的线程竞争
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章