您当前的位置: 首页 >  Java

梁云亮

暂无认证

  • 3浏览

    0关注

    1211博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Java线程池 之 ThreadPoolExecutor

梁云亮 发布时间:2019-10-30 12:09:36 ,浏览量:3

Java线程池 之 ThreadPoolExecutor

阿里巴巴的Java开发手册规定:

  • 【强制】对于服务端需要长期运行的程序,线程池不允许使用Executors.newXXXThreadPool()创建,因为这种方式会使用无界的任务队列,为避免OOM,建议通过ThreadPoolExecutor的构造方法创建,这样可以让使用者更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端: 1)newFixedThreadPool和newSingleThreadExecutor: 主要问题是允许的请求队列的长度为Integer.MAX_VALUE,可能会堆积大量的请求,会耗费非常大的内存,甚至OOM。 2)newScheduledThreadPool和newCachedThreadPool: 主要问题是允许的创建线程数量为Integer.MAX_VALUE,可能会创建数量大量的线程,甚至OOM。
  • 【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。

创建线程池的正确姿势是直接调用ThreadPoolExecutor的构造函数创建线程池。在创建的同时,指定线程的数量和BlockQueue容量就可以了,该构造方法的定义如下:

public ThreadPoolExecutor(int corePoolSize,     //核心线程数(线程池长期维护的线程数),一线程处于空闲状态,也不会回收
                          int maximumPoolSize,  //最大线程数
                          long keepAliveTime,   //超过corePoolSize的线程的空闲时长,超过这个时间,多余的线程会被回收
                          TimeUnit unit,        //时间单位 
                          BlockingQueue workQueue,    //线程等待队列
                          ThreadFactory threadFactory,          //线程工厂,新线程的产生方式
                          RejectedExecutionHandler handler)     //线程等待队列已满时的拒绝策略:当任务太多来不及处理时,如何拒绝任务;

其中:

  • corePoolSize:线程池中的线程数量,该数量值决定了新添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去;
  • maximumPoolSize:线程池中的最大线程数量,该参数会根据使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量;
  • workQueue:任务队列,被添加到线程池中,但尚未被执行的任务,它一般分为:
    • 直接提交队列:SynchronousQueue
    • 有界任务队列:ArrayBlockingQueue
    • 无界任务队列:LinkedBlockingQueue
    • 优先任务队列:PriorityBlockingQueue
  • threadFactory:线程工厂,用于创建线程,一般用默认即可;
  • handler:拒绝策略,当任务太多来不及处理时,如何拒绝任务。

注意:

  • corePoolSize和maximumPoolSize:设置不当会影响效率,甚至耗尽线程;
  • workQueue:设置不当容易导致OOM;
  • handler:设置不当会导致提交任务时抛出异常。

线程池的工作顺序:corePoolSize -> 任务队列 -> maximumPoolSize -> 拒绝策略

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

可以向线程池提交的任务有Runnable和Callable两种,其中,Callable是JDK1.5时加入的接口,作为Runnable的一种补充,允许有返回值,允许抛出异常。

ThreadPoolExecutor提供了以下三种提交任务的方法

  • void execute(Runnable command);
  • Future submit(Callable task)
  • Future submit(Runnable task) :虽然返回Future,但是其get()方法总是返回null
  • Future submit(Runnable task, T result)
任务队列(workQueue)

若无特别说明,下面示例中使用到的ThreadTask的代码如下:

public class ThreadTask implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
示例1、直接提交队列(SynchronousQueue)

直接提交队列是一个特殊的BlockingQueue,它没有容量,每执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。 使用SynchronousQueue队列,提交的任务不会被保存起来,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的进程;如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略。 在这种情况下,需要对程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执行拒绝策略。

public static void main(String[] args) {
    ExecutorService pool = new ThreadPoolExecutor(1, 2, 1000,
            TimeUnit.MILLISECONDS,
            new SynchronousQueue(),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );
    for (int i = 0; i             
关注
打赏
1665409997
查看更多评论
0.1829s