2019独角兽企业重金招聘Python工程师标准>>>

疑问

  1. 线程池中的线程是如何实现一个线程执行多个任务的?

  2. 构造线程池时为何要用阻塞队列作为参数,非阻塞队列不行吗?

线程池的几个重要参数

  • corePoolSize 是线程池的核心线程数,通常线程池会维持这个线程数

  • maximumPoolSize 是线程池所能维持的最大线程数

  • keepAliveTime 和 unit 则分别是超额线程的空闲存活时间数和时间单位

  • workQueue 是提交任务到线程池的入队列

  • threadFactory 是线程池创建新线程的线程构造器

  • handler 是当线程池不能接受提交任务的时候的处理策略

线程池处理任务一般流程

  1. 如果当前线程池中的线程数目小于corePoolSize,则每来一个任务,就会创建一个线程去执行这个任务;

  2. 如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一般来说是任务缓存队列已满),则会尝试创建新的线程去执行这个任务;

  3. 如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策略进行处理;

  4. 如果线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止。

分析

addIfUnderPoolSize

以最简单的poolSize<corePoolSize的情况来分析好了,下面是关键代码

private boolean addIfUnderCorePoolSize(Runnable firstTask) {
Thread t = null;final ReentrantLock mainLock = this.mainLock;
mainLock.lock();try {    if (poolSize < corePoolSize && runState == RUNNING)t = addThread(firstTask);        //创建线程去执行firstTask任务   } finally {mainLock.unlock();
}if (t == null)    return false;
t.start();return true;
}

addThread

看见了熟悉的t.start();,但是有一个问题,任务执行完线程就应该被销毁才对,为什么线程池里的线程能够做到执行多个任务呢? t = addThread(firstTask); 里肯定大有名堂。

private Thread addThread(Runnable firstTask) {
Worker w = new Worker(firstTask);
Thread t = threadFactory.newThread(w);  //创建一个线程,执行任务   if (t != null) {w.thread = t;            //将创建的线程的引用赋值为w的成员变量       workers.add(w);    int nt = ++poolSize;     //当前线程数加1       if (nt > largestPoolSize)largestPoolSize = nt;
}return t;
}

Worker

上面的代码中出现了Worker这个类,来看看Worker的结构:

private final class Worker implements Runnable {private final ReentrantLock runLock = new ReentrantLock();private Runnable firstTask;volatile long completedTasks;Thread thread;Worker(Runnable firstTask) {this.firstTask = firstTask;}boolean isActive() {return runLock.isLocked();}void interruptIfIdle() {final ReentrantLock runLock = this.runLock;if (runLock.tryLock()) {try {if (thread != Thread.currentThread())thread.interrupt();} finally {runLock.unlock();}}}void interruptNow() {thread.interrupt();}private void runTask(Runnable task) {final ReentrantLock runLock = this.runLock;runLock.lock();try {if (runState < STOP &&Thread.interrupted() &&runState >= STOP)boolean ran = false;beforeExecute(thread, task);   //beforeExecute方法是ThreadPoolExecutor类的一个方法,没有具体实现,用户可以根据//自己需要重载这个方法和后面的afterExecute方法来进行一些统计信息,比如某个任务的执行时间等           try {task.run();ran = true;afterExecute(task, null);++completedTasks;} catch (RuntimeException ex) {if (!ran)afterExecute(task, ex);throw ex;}} finally {runLock.unlock();}}public void run() {try {Runnable task = firstTask;firstTask = null;while (task != null || (task = getTask()) != null) {runTask(task);task = null;}} finally {workerDone(this);   //当任务队列中没有任务时,进行清理工作       }}
}

关于Worker这个类,有几点需要注意:

  1. Worker实现了runnable接口,能作为实例化thread的参数存在,thread.start()后执行的其实是Worker的run()方法

  2. 在Worker的run()方法中,有一段代码非常关键

    while (task != null || (task = getTask()) != null) {runTask(task);task = null;}

    task!=nulll好理解,task = getTask()) != null又是什么意思呢?不罗嗦,继续跟代码

Runnable getTask() {for (;;) {try {int state = runState;if (state > SHUTDOWN)return null;Runnable r;if (state == SHUTDOWN)  // Help drain queuer = workQueue.poll();else if (poolSize > corePoolSize || allowCoreThreadTimeOut) //如果线程数大于核心池大小或者允许为核心池线程设置空闲时间,//则通过poll取任务,若等待一定的时间取不到任务,则返回nullr = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);elser = workQueue.take();if (r != null)return r;if (workerCanExit()) {    //如果没取到任务,即r为null,则判断当前的worker是否可以退出if (runState >= SHUTDOWN) // Wake up othersinterruptIdleWorkers();   //中断处于空闲状态的workerreturn null;}// Else retry} catch (InterruptedException ie) {// On interruption, re-check runState}}
}

workQueue

workQueue是BlockingQueue的一个实例,而BlockingQueue的take()方法当队列为空时会使当前线程发生阻塞
getTask()的功能就是去缓存队列获取任务,如果缓存队列为空,则处于等待状态,否则拿到task回去执行runTask()方法,在runTask()方法里执行我们丢给线程池的任务的run()方法完成任务【兜了这么一大圈子】
通过while循环使这个过程不断进行

 while (task != null || (task = getTask()) != null) {runTask(task);task = null;}

总结

其实把整个流程梳理一遍就是,将要执行的任务封装进Worker对象中,Worker实现Runnable接口重写run()方法实现不断获取新任务(不管是直接获得还是来自缓存队列)的逻辑,将Worker作为参数实例化Thread对象,这样在开启线程后Worker的run方法就被间接调用了。至此,第一个问题就解决了。至于第二个问题,如果熟悉生产者消费者模式的话也不难理解。

参考资料:

  • Java并发编程:线程池的使用

  • ThreadPoolExecutor的应用和实现分析

转载于:https://my.oschina.net/u/2529036/blog/625295

线程池的一些疑问和解答相关推荐

  1. 线程池之工作项,等待项,计时项 (存在疑问???)

    线程池函数允许我们做: 1.以异步方式调用函数  //工作项 2.每隔一段时间调用一个函数 //计时项 3.在内核对象触发时调用一个函数 //等待项 4.在异步I/O请求完成时调用一个函数 //I/O ...

  2. c语言 线程a每隔10秒执行一次,线程b每隔100秒执行一次,线程池执行时多线程每隔100ms执行一次线程任务 求解答...

    MobileData data = listData.get(i); //data.setI(i); //Thread.sleep(100);//多线程调用接口的时候每隔100ms调用一次 //多线程 ...

  3. 面试官问:线程池是如何重复利用空闲的线程来执行任务的?

    欢迎关注方志朋的博客,回复"666"获面试宝典 在Java开发中,经常需要创建线程去执行一些任务,实现起来也非常方便,但如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任 ...

  4. Java线程池 源码分析

    1.个人总结及想法: (1)ThreadPoolExecutor的继承关系? ThreadPoolExecutor继承AbstractExectorService,AbstractExecutorSe ...

  5. 线程池是如何重复利用空闲的线程来执行任务的?

    来源:blog.csdn.net/anhenzhufeng/article/details/88870374 在Java开发中,经常需要创建线程去执行一些任务,实现起来也非常方便,但如果并发的线程数量 ...

  6. 万字图文 | 学会Java中的线程池,这一篇也许就够了!

    来源:一枝花算不算浪漫 线程池原理思维导图.png 前言 Java中的线程池已经不是什么神秘的技术了,相信在看的读者在项目中也都有使用过.关于线程池的文章也是数不胜数,我们站在巨人的肩膀上来再次梳理一 ...

  7. Java线程池的知识

    1.为什么要用线程池 一些任务适合使用单独的线程去执行,而线程作为一项比较重的资源如果频繁创建对系统资源消耗较大.使用线程池,将线程重复使用,节省频繁创建线程的开销. 创建一个线程需要调用操作系统的A ...

  8. mongodb线程池_常用高并发网络线程模型设计及MongoDB线程模型优化实践

    服务端通常需要支持高并发业务访问,如何设计优秀的服务端网络IO工作线程/进程模型对业务的高并发访问需求起着至关重要的核心作用. 本文总结了了不同场景下的多种网络IO线程/进程模型,并给出了各种模型的优 ...

  9. 线程池的一个BUG,被我发现了

    欢迎关注方志朋的博客,回复"666"获面试宝典 前几天,在帮同事排查一个线上偶发的线程池错误 逻辑很简单,线程池执行了一个带结果的异步任务.但是最近有偶发的报错: java.uti ...

最新文章

  1. Luke 5—— 可视化 Lucene 索引查看工具,可以查看ES的索引
  2. 盘点CVPR 2019影响力最大的20篇论文
  3. 第二次作业(个人项目实践)
  4. 数据之光 · 安全未来 | 第四届中国数据安全治理高峰论坛圆满召开!
  5. 前端学成什么样就能找工作了?
  6. 干货| 掌握这种逻辑思维,大厂面试成功率可提升90%
  7. Leetcode 99. 恢复搜索二叉树
  8. 基于C#的MongoDB数据库开发应用(2)--MongoDB数据库的C#开发
  9. 阶段3 1.Mybatis_05.使用Mybatis完成CRUD_7 Mybatis中参数的深入-使用实体类的包装对象作为查询条件...
  10. chrome浏览器使用方法介绍
  11. springboot下生成复杂word文档方案 在Word软件里面制作模板
  12. Hive 如何设置ReduceTask的数量
  13. 去除input边框和去除当点击input框时显示的边框
  14. 如何下载建外街道卫星地图高清版大图
  15. Scala Case Class介绍
  16. 【TS TSP】基于matlab禁忌搜索求解旅行商问题【含Matlab源码 447期】
  17. html国内旅游计划,Web实验一 国内旅游界面
  18. Google PR值的详细算法
  19. excel做地图热力图_中国数据地图(热力图)-到市级-分档填色
  20. Webots+tesla+ROS2

热门文章

  1. Linux下配置rdate时间服务器
  2. ArrayList使用方法
  3. windows 7 全屏游戏解决方案
  4. Callable、Future和FutureTask
  5. minicom HOWTO
  6. 提升磁盘IO性能的几个技巧
  7. RESTful API 设计指南(转)
  8. CAD绘图软件中如何查询图纸的版本是多少
  9. PWA之push服务
  10. Linux文件名命名规范