Exectors

本文深入探讨了Java中的ExecutorService和ThreadPoolExecutor,讲解了如何通过Executors创建线程池,重点分析了ThreadPoolExecutor的参数设置技巧,如核心线程数、最大线程数、存活时间等,并通过实例展示了线程池如何根据任务数量动态调整线程。同时,提到了线程池的拒绝策略及其影响。
摘要由CSDN通过智能技术生成

一 Exectors

Exector 接口:运行新任务的简单接口
ExectorService 接口:扩展了Exector接口,添加了一些用来管理执行器生命周期和任务生命周期的方法
ScheduledExecutorService 接口:扩展自ExectorService接口,支持Future和定期执行任务
Exectors 类:包装了具体的几个常用的线程池的定义,便于使用

public class Executors { 

public static ExecutorService newFixedThreadPool(int nThreads) { 
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new     
       LinkedBlockingQueue()); 
}

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), threadFactory); } 

public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }  


public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), threadFactory)); } 

 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }  

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue(), threadFactory); }


}

注意到默认由executors创建的线程池的工作队列都是基于链表的阻塞队列,没有具体长度

具体的调用方法又可以是在Exectors类中,只是提供更加便利的线程池调用方法,如果希望使用更加灵活自定义的线程池,还是建议使用ThreadPoolExecutor

二、ThreadPoolExecutor线程池参数设置技巧

public ThreadPoolExecutor(int corePoolSize,  //线程池核心线程数量
                              int maximumPoolSize,   //线程池最大线程数量
                              long keepAliveTime,      //线程KeepAlive时间,当线程池数量超过核心线程数量以后,idle时间超过这个值的线程会被终止
                              TimeUnit unit,              //线程KeepAlive时间单位
                              BlockingQueue<Runnable> workQueue,    //任务队列
                              ThreadFactory threadFactory,                  //创建线程的工厂对象
                              RejectedExecutionHandler handler)          //任务被拒绝后调用的handler

ThreadPoolExecutor类可设置的参数主要有:

  • corePoolSize

在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务(除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程)。

默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。

  • maxPoolSize
当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。
  • keepAliveTime

当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。

  • allowCoreThreadTimeout

是否允许核心线程空闲退出,默认值为false。

  • queueCapacity

任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。

还有就是 workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

 
 
  1. ArrayBlockingQueue;

  2. LinkedBlockingQueue;

  3. SynchronousQueue;

  4. PriorityBlockingQueue 

ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。
  • threadFactory:线程工厂,主要用来创建线程;
  • handler:表示当拒绝处理任务时的策略,有以下四种取值:
 
 
  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。

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

  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务(将队列最前面的任务删除,将未放入队列中的新的任务,插入到队列尾部),然后重新尝试执行任务(重复此过程)

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

线程池按以下行为执行任务

  1. 当线程数小于核心线程数时,创建线程。
  2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  3. 当线程数大于等于核心线程数,且任务队列已满
    1. 若线程数小于最大线程数,创建线程
    2. 若线程数等于最大线程数,抛出异常,拒绝任务

三 实例

public class ThreadPool {

    public static void main(String[] args) {
        // 创建线程池 , 参数含义 :(核心线程数,最大线程数,加开线程的存活时间,时间单位,任务队列长度)
        ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 8,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(2));        //设置a的值范围在:a = (corePoolSize-1) ~ (max+queue+1) ,分析:任务数 与 活跃线程数,核心线程数,队列长度,最大线程数的关系。
        int a = 7;

        for (int i = 1; i <= a; i++) {
            int j = i;
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    //获取线程名称
                    Thread thread = Thread.currentThread();
                    String name = thread.getName();
                    //输出
                    int activeCount = pool.getActiveCount();
                    System.out.println("任务:"+j+"-----,线程名称:"+name+"-----活跃线程数:"+activeCount);
                }
            });
        }
        //关闭线程池
        pool.shutdown();
    }
}

输出结果,观察关系:

//任务数 a = 4 , 活跃线程数4 , 任务数 < 核心线程数。
//任务数 a = 5 , 活跃线程数5 , 任务数 = 核心线程数。
//任务数 a = 6 , 活跃线程数5 , 任务数 < 核心线程数5 + 队列长度2 。
//任务数 a = 7 , 活跃线程数5 , 任务数 = 核心线程数5 + 队列长度2 。

//任务数 a = 8 , 活跃线程数6 , 任务数 < 最大线程数8 + 队列长度2 . 活跃线程数是在核心线程数5的基础上.加1个活跃线程。
//任务数 a = 9 , 活跃线程数7 , 任务数 < 最大线程数8 + 队列长度2.  活跃线程数是在核心线程数5的基础上.加2个活跃线程。
//任务数 a = 10 , 活跃线程数8 , 任务数 = 最大线程数8 + 队列长度2.  活跃线程数是在核心线程数5的基础上.加3个活跃线程。

//任务数 a = 11 , 活跃线程数8 , 任务数 > 最大线程数8 + 队列长度2 。抛出异常RejectedExecutionException

总结:

1、随着任务数量的增加,会增加活跃的线程数。

2、当活跃的线程数 =   核心线程数,此时不再增加活跃线程数,而是往任务队列里堆积。

3、当任务队列堆满了,随着任务数量的增加,会在核心线程数的基础上加开线程。

4、直到活跃线程数 = 最大线程数,就不能增加线程了。

5、如果此时任务还在增加,则: 任务数11 > 最大线程数8 + 队列长度2 ,抛出异常RejectedExecutionException,拒绝任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值