ThreadPoolExecutor简介

  1. 并发包中提供的一个线程池服务

线程池的工作过程

  1. 线程池刚创建,里面没有线程.任务队列是作为参数传进来的.线程池不会立即执行任务.
  2. 调用execute()方法添加一个任务,线程池会做如下判断:
    • 如果正在运行的线程数量小于corePoolSize,马上创建线程运行这个任务
    • 如果正在运行的线程数量大于或等于corePoolSize,这个任务放入队列
    • 如果队列满了,且正在运行的线程数量小于maximumPoolSize,就要创建线程运行这个任务
    • 如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,通过 handler所指定的策略来处理此任务
  3. 当一个线程完成任务时,会从队列取下一个任务来执行
  4. 当一个线程空闲时,超过keepAliveTime,线程池会判断,如果当前运行的线程数大于corePoolSize,这个线程会被停掉.

任务队列选择

  1. ArrayBlockingQueue
  2. LinkedBlockingQueue 没有大小限制

handler选择

  1. ThreadPoolExecutor.AbortPolicy() 抛出java.util.concurrent.RejectedExecutionException异常
  2. ThreadPoolExecutor.CallerRunsPolicy() 重试添加当前的任务,他会自动重复调用execute()方法
  3. ThreadPoolExecutor.DiscardOldestPolicy() 抛弃旧的任务
  4. ThreadPoolExecutor.DiscardPolicy() 抛弃当前的任务

源码分析

jdk1.7.0_71

构造

  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
  1. public ThreadPoolExecutor(
    int corePoolSize,//线程池维护线程的最少数量
    int maximumPoolSize,//线程池维护线程的最大数量
    long keepAliveTime,//线程池维护线程所允许的空闲时间
    TimeUnit unit,//线程池维护线程所允许的空闲时间的单位
    BlockingQueue<Runnable> workQueue,//线程池所使用的缓冲队列
    ThreadFactory threadFactory,//执行程序创建新线程时使用的工厂
    RejectedExecutionHandler handler//线程池对拒绝任务的处理策略
    ){}

execute(Runnable command) 添加任务到线程池

  1. 1
    2
    3
    4
    5
    6
  1. public void execute(Runnable command) {
    //command为空抛异常
    //如果正在运行的线程数量小于corePoolSize,会立即addWorker(),创建新线程运行
    //如果正在执行的数量大于等于corePoolSize,将任务放到阻塞队列.如果阻塞队列没有满并且是运行着的,直接放入阻塞队列.放入队列之后还要再做一次检查,如果线程池不在运行状态,把刚才的任务移除,调用reject方法,否则查看worker数量,若为0起一个新的worker去执行任务
    //加入队列失败的话,会addWorker尝试一个新的worker去执行任务,新worker创建失败,调用reject方法
    }

addWorker(Runnable firstTask, boolean core)

详细查看此文章

  1. 1
    2
    3
  1. firstTask表示需要跑的任务。
    boolean类型的core参数为true的话表示使用线程池的基本大小
    false使用线程池最大大小
  1. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
  1. private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
    int c = ctl.get();
    int rs = runStateOf(c); // 线程池当前状态

    // 这个判断转换成 rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty)。
    // 概括为3个条件:
    // 1. 线程池不在RUNNING状态并且状态是STOP、TIDYING或TERMINATED中的任意一种状态
    // 2. 线程池不在RUNNING状态,线程池接受了新的任务
    // 3. 线程池不在RUNNING状态,阻塞队列为空。 满足这3个条件中的任意一个的话,拒绝执行任务
    if (rs >= SHUTDOWN &&
    ! (rs == SHUTDOWN &&
    firstTask == null &&
    ! workQueue.isEmpty()))
    return false;

    for (;;) {
    int wc = workerCountOf(c); // 线程池线程个数
    if (wc >= CAPACITY ||
    wc >= (core ? corePoolSize : maximumPoolSize)) // 如果线程池线程数量超过线程池最大容量或者线程数量超过了基本大小(core参数为true,core参数为false的话判断超过最大大小)
    return false; // 超过直接返回false
    if (compareAndIncrementWorkerCount(c)) // 没有超过各种大小的话,cas操作线程池线程数量+1,cas成功的话跳出循环
    break retry;
    c = ctl.get(); // 重新检查状态
    if (runStateOf(c) != rs) // 如果状态改变了,重新循环操作
    continue retry;
    // else CAS failed due to workerCount change; retry inner loop
    }
    }
    // 走到这一步说明cas操作成功了,线程池线程数量+1
    boolean workerStarted = false; // 任务是否成功启动标识
    boolean workerAdded = false; // 任务是否添加成功标识
    Worker w = null;
    try {
    final ReentrantLock mainLock = this.mainLock; // 得到线程池的可重入锁
    w = new Worker(firstTask); // 基于任务firstTask构造worker
    final Thread t = w.thread; // 使用Worker的属性thread,这个thread是使用ThreadFactory构造出来的
    if (t != null) { // ThreadFactory构造出的Thread有可能是null,做个判断
    mainLock.lock(); // 锁住,防止并发
    try {
    // 在锁住之后再重新检测一下状态
    int c = ctl.get();
    int rs = runStateOf(c);

    if (rs < SHUTDOWN ||
    (rs == SHUTDOWN && firstTask == null)) { // 如果线程池在RUNNING状态或者线程池在SHUTDOWN状态并且任务是个null
    if (t.isAlive()) // 判断线程是否还活着,也就是说线程已经启动并且还没死掉
    throw new IllegalThreadStateException(); // 如果存在已经启动并且还没死的线程,抛出异常
    workers.add(w); // worker添加到线程池的workers属性中,是个HashSet
    int s = workers.size(); // 得到目前线程池中的线程个数
    if (s > largestPoolSize) // 如果线程池中的线程个数超过了线程池中的最大线程数时,更新一下这个最大线程数
    largestPoolSize = s;
    workerAdded = true; // 标识一下任务已经添加成功
    }
    } finally {
    mainLock.unlock(); // 解锁
    }
    if (workerAdded) { // 如果任务添加成功,运行任务,改变一下任务成功启动标识
    t.start(); // 启动线程,这里的t是Worker中的thread属性,所以相当于就是调用了Worker的run方法
    workerStarted = true;
    }
    }
    } finally {
    if (! workerStarted) // 如果任务启动失败,调用addWorkerFailed方法
    addWorkerFailed(w);
    }
    return workerStarted;
    }

参考

http://fulong258.blog.163.com/blog/static/17895044201082951820935

http://www.oschina.net/question/12_2656

http://coach.iteye.com/blog/855850

http://fangjian0423.github.io/2016/03/22/java-threadpool-analysis/

ThreadPoolExecutor简介的更多相关文章

  1. JUC线程池之 ThreadPoolExecutor简介

    ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存放一定数量线程的一个线程集合.线程池允许若个线程同时允许,允许同 ...

  2. Java多线程系列——线程池原理之 ThreadPoolExecutor

    ThreadPoolExecutor 简介 ThreadPoolExecutor 是线程池类. 通俗的讲,它是一个存放一定数量线程的线程集合.线程池允许多个线程同时运行,同时运行的线程数量就是这个线程 ...

  3. Java - "JUC线程池" ThreadPoolExecutor原理解析

    Java多线程系列--“JUC线程池”02之 线程池原理(一) ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存 ...

  4. J.U.C并发框架源码阅读(十三)ThreadPoolExecutor

    基于版本jdk1.7.0_80 java.util.concurrent.ThreadPoolExecutor 代码如下 /* * ORACLE PROPRIETARY/CONFIDENTIAL. U ...

  5. Java并发包--ThreadPoolExecutor

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509941.html ThreadPoolExecutor简介 ThreadPoolExecutor是线 ...

  6. Java线程池ThreadPoolExecutor极简教程

    ThreadPoolExecutor 简介 ThreadPoolExecutor 是 java.util.concurrent 包下的一个类,在jdk1.5版本引入,帮助开发人员管理线程并方便地执行并 ...

  7. java多线程系类:JUC线程池:02之线程池原理(一)

    在上一章"Java多线程系列--"JUC线程池"01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我 ...

  8. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  9. KClient——kafka消息中间件源码解读

    目录 kclient消息中间件 kclient-processor top.ninwoo.kclient.app.KClientApplication top.ninwoo.kclient.app.K ...

随机推荐

  1. rails小重构:将图片加入产品Model

    原先的产品product模式中存放的是图片的url,必须手动将图片存入指定目录中.现在略作改动,在数据库中新建一个pictures表,其设定如下: class CreatePictures < ...

  2. Object对象你真理解了吗?

    前言 五一回家又断更了一个放假时间了~~~ 只有光头才能变强 回顾前面: ThreadLocal就是这么简单 多线程三分钟就可以入个门了! 多线程基础必要知识点!看了学习多线程事半功倍 Java锁机制 ...

  3. Jquery测试题

    一.Jquery测试题 下面哪种不是jquery的选择器?(单选) A.基本选择器 B.后代选择器 C.类选择器 D.进一步选择器 考点:jquery的选择器 (C) 当DOM加载完成后要执行的函数, ...

  4. javascript学习(一)构建自己的JS库

    库是一个饱受争议的热门话题.一种观点认为它是一种非常棒的工具,是任何开发者都不可或缺的:另一种观点则认为在不理解库的内部工作原理的情况下对库形成依赖,会助长懒惰的风气从而导致开发者素质下降 库是一个饱 ...

  5. .haccess 配置生效

    前言:网上看到有写用.haccess会影响到网站速率之类的,这里只是讲解自己的一点配置.haccess的心得,其它的之后再讨论哈~ 1.普通配置: 网上都有的那种配置   apache/conf/ht ...

  6. Spring Boot开发MongoDB应用实践

    本文继续上一篇定时任务中提到的邮件服务,简单讲解Spring Boot中如何使用MongoDB进行应用开发. 上文中提到的这个简易邮件系统大致设计思路如下: 1.发送邮件支持同步和异步发送两种 2.邮 ...

  7. MySql foreach属性

    foreach属性 属性 描述 item 循环体中的具体对象.支持属性的点路径访问,如item.age,item.info.details.具体说明:在list和数组中是其中的对象,在map中是val ...

  8. Django升级1.8的一些问题

    1.最明显的问题当然是Settings设置中关于模板的设置数据结构发生变化,这个就不细说了,你开个Django的1.8的新项目就知道怎么改了 2.migrations问题,这个问题是1.8最主要的修改 ...

  9. mac os x 10.9.3 升级到10.10.4 记录

    昨天终于忍不住,将mac pro 的操作系统从 os x 10.9.3 升级到10.10.4,因为网络不给力,500k/s,光系统包都要5.6G,所以整整下来了一个工作白天,等下班的时候开始安装,不过 ...

  10. require、缓存

    什么是require? -Node使用CommonJS模块规范,内置require函数用于加载模块文件 -require的基本功能是>读入并执行一个javascript文件,然后返回该模块的ex ...