转载  http://www.cnblogs.com/skywang12345/p/3509960.html ; http://www.cnblogs.com/skywang12345/p/3509941.html

ThreadPoolExecutor简介

ThreadPoolExecutor是线程池类。对于线程池,可以将它理解为"存放一定数量线程的一个线程集合。线程池允许若个线程同时运行,通过线程池对运行的线程进行管理"。

线程池的生命周期

线程池的5种状态是:RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED。

线程池状态定义代码如下:

    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; }

说明

ctl是一个AtomicInteger类型的原子对象。ctl记录了"线程池中的任务数量"和"线程池状态"2个信息。
ctl共包括32位。其中,高3位表示"线程池状态",低29位表示"线程池中的任务数量"。

RUNNING    -- 对应的高3位值是111。
SHUTDOWN -- 对应的高3位值是000。
STOP -- 对应的高3位值是001。
TIDYING -- 对应的高3位值是010。
TERMINATED -- 对应的高3位值是011。

程池各个状态之间的切换如下图所示:

1. RUNNING

(01) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对阻塞队列中的任务进行处理。
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态!
道理很简单,在ctl的初始化代码中(如下),就将它初始化为RUNNING状态,并且"任务数量"初始化为0。

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

2. SHUTDOWN

(01) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能对阻塞队列中的任务进行处理。
(02) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

3. STOP

(01) 状态说明:线程池处在STOP状态时,不接收新任务,不处理阻塞队列中的任务,并且尝试中断正在处理的任务。
(02) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

4. TIDYING
(01) 状态说明:当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
(02) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

5. TERMINATED
(01) 状态说明:线程池彻底终止,就变成TERMINATED状态。
(02) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

ThreadPoolExecutor数据结构

ThreadPoolExecutor.java中的成员变量定义如下:

private final BlockingQueue<Runnable> workQueue;// 阻塞队列。

private final ReentrantLock mainLock = new ReentrantLock();// 互斥锁

private final HashSet<Worker> workers = new HashSet<Worker>();// 线程集合。一个Worker对应一个线程。

private final Condition termination = mainLock.newCondition();// “终止条件”,与“mainLock”绑定。

private int largestPoolSize;// 线程池中线程数量曾经达到过的最大值。

private long completedTaskCount;// 已完成任务数量

private volatile ThreadFactory threadFactory;// ThreadFactory对象,用于创建线程。

private volatile RejectedExecutionHandler handler;// 拒绝策略的处理句柄。

private volatile long keepAliveTime;// 线程没有任务执行保持的存活时间。

private volatile boolean allowCoreThreadTimeOut;

private volatile int corePoolSize;// 核心池大小

private volatile int maximumPoolSize;// 最大池大小

1. workers
    workers是HashSet<Work>类型,即它是一个Worker集合。一个Worker对应一个线程,也就是说线程池通过workers包含了"一个线程集合"。当Worker对应的线程池启动时,它会执行线程池中的任务;当执行完一个任务后,它会从线程池的阻塞队列中取出一个阻塞的任务来继续运行。

2. workQueue

    workQueue是BlockingQueue 类型,即用来保存等待被执行的任务的阻塞队列. 在JDK中提供了如下阻塞队列: 
    (1) ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务; 
    (2) LinkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene; 
    (3) SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene; 
  (4) priorityBlockingQuene:具有优先级的无界阻塞队列;

3. mainLock

mainLock是互斥锁,通过mainLock实现了对线程池的互斥访问。

4. corePoolSize
    线程池中的核心线程数,在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,即使有其他空闲线程能够执行新来的任务, 也会继续创建线程;当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到阻塞队列当中。

5. maximumPoolSize

   线程池最大线程数,它表示在线程池中最多能创建多少个线程 ,例如,当新任务提交给线程池时(通过execute方法) , 如果线程池中运行的线程数量大于maximumPoolSize;则拒绝将要加入的任务,并执行相应的拒绝策略。当阻塞队列是无界队列, 则maximumPoolSize则不起作用, 因为无法提交至核心线程池的线程会一直持续地放入阻塞队列.

  如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心池大小和最大池大小的值是在创建线程池设置的;但是,也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。

6. poolSize
    poolSize是当前线程池的实际大小,即线程池中任务的数量。

7. allowCoreThreadTimeOut 
    allowCoreThreadTimeOut表示是否允许"线程在空闲状态时,仍然能够存活";

8. keepAliveTime

 表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

9. unit

 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

TimeUnit.DAYS;               //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒

10. threadFactory
    threadFactory是ThreadFactory对象。它是一个线程工厂类,"线程池通过ThreadFactory创建线程 ,并设置一个具有识别度的线程名"。默认为 defaultThreadFactory

11. handler

handler是RejectedExecutionHandler类型。它是"线程池拒绝策略"的句柄,也就是说"当某任务添加到线程池中,而线程池拒绝该任务时,线程池会通过handler进行相应的处理"。
     线程池提供了4种策略:
    (1) AbortPolicy:直接抛出异常,默认策略;
    (2) CallerRunsPolicy:用调用者所在的线程来执行任务;
    (3) DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
    (4) DiscardPolicy:直接丢弃任务;当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义拒绝策略,如记录日志或持久化存储不能处理的任务。

综上所说,线程池通过workers来管理"线程集合",每个线程在启动后,会执行线程池中的任务;当一个任务执行完后,它会从线程池的阻塞队列中取出任务来继续运行。阻塞队列是管理线程池任务的队列,当添加到线程池中的任务超过线程池的容量时,该任务就会进入阻塞队列进行等待。


“泰戈尔说,不要着急,最好的总会在最不经意的时候出现。那我们要做的就是:怀揣希望去努力,静待美好的出现。”

Java多线程系列 JUC线程池02 线程池原理解析(一)的更多相关文章

  1. Java多线程系列--“JUC原子类”02之 AtomicLong原子类

    概要 AtomicInteger, AtomicLong和AtomicBoolean这3个基本类型的原子类的原理和用法相似.本章以AtomicLong对基本类型的原子类进行介绍.内容包括:Atomic ...

  2. Java多线程系列--“JUC原子类”04之 AtomicReference原子类

    概要 本章对AtomicReference引用类型的原子类进行介绍.内容包括:AtomicReference介绍和函数列表AtomicReference源码分析(基于JDK1.7.0_40)Atomi ...

  3. Java多线程系列--“JUC原子类”03之 AtomicLongArray原子类

    概要 AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray这3个数组类型的原子类的原理和用法相似.本章以AtomicLongArray对数 ...

  4. Java多线程系列--“JUC原子类”05之 AtomicLongFieldUpdater原子类

    概要 AtomicIntegerFieldUpdater, AtomicLongFieldUpdater和AtomicReferenceFieldUpdater这3个修改类的成员的原子类型的原理和用法 ...

  5. java 多线程系列---JUC原子类(三)之AtomicLongArray原子类

    AtomicLongArray介绍和函数列表 在"Java多线程系列--“JUC原子类”02之 AtomicLong原子类"中介绍过,AtomicLong是作用是对长整形进行原子操 ...

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

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

  7. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

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

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  9. Java多线程系列--“JUC线程池”04之 线程池原理(三)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...

  10. Java多线程系列--“JUC线程池”05之 线程池原理(四)

    概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...

随机推荐

  1. jdk1.6 升级到 jdk1.7

    将 jdk1.6 升级到 jdk1.7 下载jdk-7u11-linux-x64.tar.gz   tar zxvf  jdk-7u11-linux-x64.tar.gz mkdir   /usr/l ...

  2. spark插件入门完整版本

    1 在spark项目中添加source folder文件夹,取名为src/plugins/testplugin/src/java 2 在此文件夹下新建包名,取名为com.jivesoftware.sp ...

  3. SpringSecurity学习四----------基于不同角色跳转到不同URL

    © 版权声明:本文为博主原创文章,转载请注明出处 1.项目结构 2.pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0& ...

  4. Laravel5.4使用Memcached缓存

    修改默认的缓存驱动 Laravel默认的缓存驱动是file,想要切换为Memcached需要修改.env文件.把CACHE_DRIVER=file改为CACHE_DRIVER=memcached,改好 ...

  5. ISE封装IP

    1.综合成ngc文件,然后再黑盒调用,再写一个端口文件(写个空壳文件,就是定义输入输出,在工程里面调用这个文件就行,把ngc放到工程目录下).

  6. PHPthinking为全部PHP爱好者指路

    1.新手学习路线:个人觉得.自己手上应该有本參考书.像细说PHP,然后看书学习,穿插网上的一些免费视频教程(随便百度一下即可,之后我尽量整理一些给大家).当然假设自控能力差的同学.也能够參加一些培训机 ...

  7. pip或者anacnda安装opencv以及opencv-contrib

    一条命令就可以搞定: pip install opencv-contrib-python   opencv-python 或者: pip install opencv-contrib-python== ...

  8. Java编程之路相关书籍(三个维度)

    一.关于Java的技术学习.能够依照以下分三个维度进行学习 : (1)向下发展,也就是底层的方向 建议看<深入Java虚拟机>.<Java虚拟机规范>.<Thinking ...

  9. shadow密码文件

    登录Linux会要求输入用户名和密码.通常本地文件中会存储一份用户密码,并与用户输入对比,如果相同就允许用户登录.起初用户密码存储与/etc/passwd中,但由于/etc/passwd必须供所有用户 ...

  10. 《TomCat与Java Web开发技术详解》(第二版) 第八章节的学习总结 -- 访问mysql

    终于学到如何访问Mysql了 1. 可以看看此章节提供的sql脚本,以后可以照着写了.此外,对于Mysql如何使用,最好的地方就是其官网介绍了.http://dev.mysql.com/doc/ref ...