美文网首页
ThreadPoolExecutor用法解析

ThreadPoolExecutor用法解析

作者: 猪的尾巴 | 来源:发表于2017-09-10 21:18 被阅读0次

大概用Executors直接生成ThreadPoolExecutor的习惯会被认为理所当然,但是在阿里的java开发规范中是禁止的,开发者必须使用ThreadPoolExecutor。其实没有什么好不好,Executors没法满足所有场景罢了。

先贴一张网上找来的图介绍下线程池运行线程的流程

image.png

ThreadPoolExecutor

司空见惯,ThreadPoolExecutor也有多个构造方法,并且都指向参数最多的一个:

image.png

下面贴出这个构造方法的代码:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

逐一解释

corePoolSize

核心线程数,当线程池初始化以后,不会立即创建所有的核心线程(除非主动调用了prestartAllCoreThreads()),而是有一个任务进来,如果线程数还没有达到corePoolSize,那么就选择新建一个线程,如果达到核心线程数,则放入阻塞队列中。


maximumPoolSize

因为corePoolSize的设置,溢出任务会被放入阻塞队列中,当阻塞队列满了,同时又有新任务进来,那么线程池会进行扩容,但是总的线程数量不会超过maximumPoolSize。


keepAliveTime与unit

keepAliveTime代表当线程空闲以后(前提是之前发生过阻塞队列满,并且增加了线程数量的情况),还能继续存活的时间,unit是时间单位,当时间超过keepAliveTime并且线程处于空闲状态,则被回收,回收的数量为:当前线程数 - corePoolSize。


workQueue

当前已有workQueue个线程在执行任务无法空闲,此时如果新加入任务,则放入workQueue(阻塞队列中),下面介绍几种阻塞队列。

队列 说明
ArrayBlockingQueue 基于数组结构的有界阻塞队列,按FIFO排序任务
LinkedBlockingQuene 基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene
SynchronousQuene 一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene
priorityBlockingQuene priorityBlockingQuene:具有优先级的无界阻塞队列

threadFactory

线程工厂,主要对线程进行一些装饰,如我需要对我的线程有特殊的命名:

  static class TestThreadFactory implements ThreadFactory {
    private AtomicInteger threadNum = new AtomicInteger(0);
    public Thread newThread(Runnable r) {
      Thread t = new Thread(r);
      t.setName("test-thread-pool-" + threadNum.incrementAndGet());
      return t;
    }
  }   

handler

当线程池无法承受任务数量(当前线程数量 = maximumPoolSize),再有任务进来,将会采取拒绝策略,java提供了四种对超额任务的拒绝处理

队列 说明
ThreadPoolExecutor.AbortPolicy 丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy 也是丢弃任务,但是不抛出异常
ThreadPoolExecutor.DiscardOldestPolicy 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy 由调用线程处理该任务

一个简单的例子

public class test {

  public static void main(String[] args){
    TestThreadFactory testThreadFactory = new TestThreadFactory();

    // 默认策略,直接抛出异常
    RejectedExecutionHandler abortPolicyHandler = new ThreadPoolExecutor.AbortPolicy();

    // 丢弃任务,不抛出异常
    RejectedExecutionHandler discardPolicyHandler = new ThreadPoolExecutor.DiscardPolicy();

    // 丢弃队列最前面的任务,重新尝试执行任务
    RejectedExecutionHandler discardOldestPolicyHandler = new ThreadPoolExecutor.DiscardOldestPolicy();

    // 由调用线程处处理该任务-移交给主线程执行(谨慎使用)
    RejectedExecutionHandler CallerRunsPolicyHandler = new ThreadPoolExecutor.CallerRunsPolicy();


    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,
                                                                   10,
                                                                   1,
                                                                   TimeUnit.SECONDS,
                                                                   new ArrayBlockingQueue<>(
                                                                       1),
                                                                   testThreadFactory,
                                                                   CallerRunsPolicyHandler);
    System.out.println(threadPoolExecutor.getPoolSize());
    for (int i = 0; i <= 15; i++){
      threadPoolExecutor.execute(() -> {
        System.out.println("hello Jules, I am [" + Thread.currentThread().getName() + "]");
        try {
          Thread.sleep(2000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      });
    }
    try {
      Thread.sleep(4000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(threadPoolExecutor.getPoolSize());

  }

  /**
   * 利用TestThreadFactory修改线程名称
   */
  static class TestThreadFactory implements ThreadFactory {
    private AtomicInteger threadNum = new AtomicInteger(0);
    public Thread newThread(Runnable r) {
      Thread t = new Thread(r);
      t.setName("test-thread-pool-" + threadNum.incrementAndGet());
      return t;
    }
  }
}

结果:

image.png

采用了CallerRunsPolicyHandler策略,所以溢出线程由调用方(main)处理。


欢迎关注公众号交流,定期分享源码心得


image.png

相关文章

  • ThreadPoolExecutor用法解析

    大概用Executors直接生成ThreadPoolExecutor的习惯会被认为理所当然,但是在阿里的java开...

  • java线程池详解

    参考文章:ThreadPoolExecutor使用详解参考文章:ThreadPoolExecutor线程池解析与B...

  • ThreadPoolExecutor解析

    为什么要使用线程池? 引用自http://ifeve.com/java-threadpool/的说明: 1.频繁的...

  • ThreadPoolExecutor 解析

    线程池相对于手动创建线程有以下优势: 减少创建销毁线程的额外消耗。 CPU资源有限,创建的线程过多,有的任务不能及...

  • ThreadPoolExecutor的用法

    Java中的线程池 一般我们说起Java中的线程池,其实指的是java.util.concurrent包下的Thr...

  • ThreadPoolExecutor线程池原理

    本文参考Java线程池---addWorker方法解析Java线程池ThreadPoolExecutor实现原理线...

  • ThreadPoolExecutor源码解析

    一. 线程池概述 在介绍线程池之前,先说一下为什么在开发中要使用线程池,个人认为主要有一下的原因: 开发中使用线程...

  • ThreadPoolExecutor源码解析

      无论是直接还是间接的创建线程池,归根结底都是通过ThreadPoolExecutor来创建线程池并且配置线程池...

  • ThreadPoolExecutor源码解析

    引言 为什么引入线程池技术? 对于服务端的程序,经常面对的是执行时间较短、工作内容较为单一的任务,需要服务端快速处...

  • ThreadPoolExecutor源码解析

    1. 简介 在引入线程池之前,我们先来了解几个事情: 线程的创建和销毁是有代价的,如线程创建需要时间和相关计算资源...

网友评论

      本文标题:ThreadPoolExecutor用法解析

      本文链接:https://www.haomeiwen.com/subject/tpytsxtx.html