java线程池

简介

线程池(英语:threadpool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销。

线程池状态和数量

java线程池经常被使用,所以我参考了一些资料,总结了一下自己掌握的部分,先看一下线程池的状态的转换:


用一个32位的二进制数来表示线程池的状态和线程数量, 高三位表示线程池的状态,后29位表示线程池的线程数量

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

线程池构造函数及参数含义


    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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • corePoolSize
    • 核心线程池数量, 当线程池数量小于corePoolSize, 有新任务的时候会直接创建一个线程, 把当前任务设为这个线程的第一个任务
  • maximumPoolSize
    • 最大线程池数量, 当线程池数量大于corePoolSize并且阻塞队列也满了,这个时候有新任务到来,会创建新的线程,但是线程数量上限不能超过maximumPoolSize
  • keepAliveTime
    • 空闲线程存活时间, 当线程池数量大于corePoolSize, 空闲线程如果超过keepAliveTime都没有新的任务, 那么这个空闲线程就会被回收
  • unit
    • 时间单位
  • workQueue
    • 阻塞队列,注意无界队列最好设置个值(避免OOM)
  • handler
    • 当线程池的数量大于maximumPoolSize, 将会采用拒绝策略
  • threadFactory
    • 这个其实挺重要的,最常用的是设置线程的名称,增强可读性方便我们查看日志或者定位问题
 ThreadFactory autoPayThreadFactory = new ThreadFactoryBuilder().setNameFormat("AutoPayThreadPool-%d").setDaemon(true).build();

阿里线程池使用规范

  1. 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
    正例:

    public class TimerTaskThread extends Thread {
         public TimerTaskThread() {
         super.setName("TimerTaskThread");
     ...
    }
    
  2. 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。

  • 说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
  1. 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。说明: Executors 返回的线程池对象的弊端如下:
  • FixedThreadPool 和 SingleThreadPool : 允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。
  • CachedThreadPool 和 ScheduledThreadPool :允许的创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。
文章目录
  1. 1. 简介
  2. 2. 线程池状态和数量
  3. 3. 线程池构造函数及参数含义
  4. 4. 阿里线程池使用规范