美文网首页
线程池的工作过程

线程池的工作过程

作者: 贾歌 | 来源:发表于2017-11-24 11:24 被阅读22次

线程池的工作过程

  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:

    1. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
    2. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
    3. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
    4. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常RejectExecutionException。
  3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。(这是一个非常巧妙的设计方式,假如我们来设计线程池,可能会有一个任务分派线程,当发现有线程空闲时,就从任务缓存队列中取一个任务交给空闲线程执行。但是在这里,并没有采用这样的方式,因为这样会要额外地对任务分派线程进行管理,无形地会增加难度和复杂度,这里直接让执行完任务的线程去任务缓存队列里面取任务来执行。)

  4. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

线程池状态

在ThreadPoolExecutor中定义了一个Volatile变量,另外定义了几个static final变量表示线程池的各个状态:

volatile int runState;

static final int RUNNING= 0;

static final int SHUTDOWN = 1;

static final int STOP = 2;

static final int TERMINATED = 3;

runState表示当前线程池的状态,它是一个volatile变量用来保证线程之间的可见性;

下面的几个static final变量表示runState可能的几个取值:

  1. 当创建线程池后,初始时,线程池处于RUNNING状态;
  2. 如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
  3. 如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
  4. 当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。

ThreadPoolExecutor类中的一些比较重要成员变量

  1. private final BlockingQueue<Runnable> workQueue; //任务缓存队列,用来存放等待执行的任务

  2. private final ReentrantLock mainLock = new ReentrantLock(); //线程池的主要状态锁,对线程池状态(比如线程池大小
    //、runState等)的改变都要使用这个锁

  3. private final HashSet<Worker> workers = new HashSet<Worker>(); //用来存放工作集

  4. private volatile long keepAliveTime;//线程存活时间

  5. private volatile boolean allowCoreThreadTimeOut; //是否允许为核心线程设置存活时间

  6. private volatile int corePoolSize; //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)

  7. private volatile int maximumPoolSize; //线程池最大能容忍的线程数

  8. private volatile int poolSize; //线程池中当前的线程数

  9. private volatile RejectedExecutionHandler handler; //任务拒绝策略

  10. private volatile ThreadFactory threadFactory; //线程工厂,用来创建线程

  11. private int largestPoolSize; //用来记录线程池中曾经出现过的最大线程数

  12. private long completedTaskCount; //用来记录已经执行完毕的任务个数

任务缓存队列及排队策略

在前面我们多次提到了任务缓存队列,即workQueue,它用来存放等待执行的任务。

workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:

  1. ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须指定大小;

  2. LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;

  3. synchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

任务拒绝策略

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

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

线程池的关闭

ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:

  1. shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
  2. shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

线程池容量的动态调整

ThreadPoolExecutor提供了动态调整线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

  1. setCorePoolSize:设置核心池大小
  2. setMaximumPoolSize:设置线程池最大能创建的线程数目大小

如何合理配置线程池的大小

  1. 一般需要根据任务的类型来配置线程池大小:

  2. 如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1

  3. 如果是IO密集型任务,参考值可以设置为2*NCPU

  4. 当然,这只是一个参考值,具体的设置还需要根据实际情况进行调整,比如可以先将线程池大小设置为参考值,再观察任务运行情况和系统负载、资源利用率来进行适当调整。

相关文章

  • ThreadPoolExecutor简介

    ThreadPoolExecutor简介 并发包中提供的一个线程池服务 线程池的工作过程 线程池刚创建,里面没有线...

  • 线程池的工作过程

    线程池的工作过程 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也...

  • 线程池

    线程池执行过程 线程池生命周期 线程池分类 阻塞队列 拒绝策略 - ThreadPoolExecutor.Abor...

  • JUC之线程池

    线程池 线程池做的工作主要是控制运行的线程的数量 ,处理过程中将任务加入队列 ,然后在线程创建后启动这些任务, 如...

  • 线程池学习(深入)

    一、为什么要用线程池? 线程池做的工作只是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些...

  • Java线程池的原理

    先上个网图说明下线程池的执行过程 来看下线程池的执行过程 1.当线程池数量小于核心线程数 直接创建核心线程 2.队...

  • 线程池使用及优势

    线程池使用及优势 线程池的主要工作是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如...

  • 线程池

    线程池种类 ThreadPoolExecutor 基础线程池 线程执行任务过程 当前执行线程数 < corePoo...

  • Java-ThreadPool线程池总结

    线程池的优势 线程池做的工作主要是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果...

  • pthread 创建的动态线程池

    组织结构: 1,缓存工作的任务池,任务节点 2,存储工作线程的池,线程节点 3,任务池投递到工作线程的线程 4,工...

网友评论

      本文标题:线程池的工作过程

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