1.ThreadPoolExcuter原理说明

  首先我们要知道为什么要使用ThreadPoolExcuter,具体可以看看文档中的说明:
  线程池 ThreadPoolExcuter 可以解决两个不同问题:由于减少了每个任务的调用开销,在执行大量的异步任务时,它通常能够提供更好的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。每个 ThreadPoolExecutor还维护着一些基本的统计数据,如完成的任务数。
  线程池 ThreadPoolExcuter 做的其实可以看得很简单,其实就是把你提交的任务(task)进行调度管理运行,但这个调度的过程以及其中的状态控制是比较复杂的。

2.初始化参数介绍
可以直接看最完整的ThreadPoolExcuter的初始化函数:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
...
}

逐个介绍如下:
corePoolSize:核心线程数,在ThreadPoolExcutor中有一个与它相关的配置:allowCoreThreadTimeOut(默认为false),当allowCoreThreadTimeOut为false时,核心线程会一直存活,哪怕是一直空闲着。而当allowCoreThreadTimeOut为true时核心线程空闲时间超过keepAliveTime时会被回收。

maximumPoolSize:最大线程数,线程池能容纳的最大线程数,当线程池中的线程达到最大时,此时添加任务将会采用拒绝策略,默认的拒绝策略是抛出一个运行时错误(RejectedExecutionException)。值得一提的是,当初始化时用的工作队列为LinkedBlockingDeque时,这个值将无效。

keepAliveTime:存活时间,当非核心空闲超过这个时间将被回收,同时空闲核心线程是否回收受allowCoreThreadTimeOut影响。

unit:keepAliveTime的单位。

workQueue:任务队列,常用有三种队列,即SynchronousQueue,LinkedBlockingDeque(无界队列),ArrayBlockingQueue(有界队列)。

threadFactory:线程工厂,ThreadFactory是一个接口,用来创建worker。通过线程工厂可以对线程的一些属性进行定制。默认直接新建线程。

RejectedExecutionHandler:也是一个接口,只有一个方法,当线程池中的资源已经全部使用,添加新线程被拒绝时,会调用RejectedExecutionHandler的rejectedExecution法。
默认是抛出一个运行时异常。

  这么多参数看起来好像很复杂,所以Java贴心得为我们准备了便捷的API,即可以直接用Executors创建各种线程池。分别是:

        //创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
     //通过设置corePoolSize为0,而maximumPoolSize为Integer.Max_VALUE(Int型数据最大值)实现。
     ExecutorService cache = Executors.newCachedThreadPool();
     //创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
     //通过将corePoolSize和maximumPoolSize的值设置为一样的值来实现。
ExecutorService fixed = Executors.newFixedThreadPool(num);
     //创建一个定长线程池,支持定时及周期性任务执行。
     //通过将队列参数workQueue设置为DelayWorkQueue来实现。
ExecutorService schedule = Executors.newScheduledThreadPool(5);
     //创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
     //通过将corePoolSize和maximumPoolSize都设置为1来实现。
ExecutorService single = Executors.newSingleThreadExecutor();

  这几个API会根据具体的情况而使用预设定好默认的初始化参数去创建一个ThreadPoolExecutor。

  这里需要做一个额外说明,在ThreadPoolExcuter中,worker和task是有区别的,task是用户提交的任务,而worker则是用来执行task的线程。在初始化参数中,corePoolSize和maximumPoolSize都是针对worker的,而workQueue是用来存放task的。

3.worker介绍

  前面有介绍了一下worker和task的区别,其中task是用户提交的线程任务,而worker则是ThreadPoolExecutor自己内部实现的一个类了。

  具体源码如下:

  

    /**
* Woker主要维护着运行task的worker的中断控制信息,以及其他小记录。这个类拓展AbstractQueuedSynchronizer
* 而来简化获取和释放每一个任务执行中的锁。这可以防止中断那些打算唤醒正在等待其他线程任务的任务,而不是
* 中断正在运行的任务。我们实现一个简单的不可重入锁而不是ReentrantLo,因为我们不想当其调用setCorePoolSize
* 这样的方法的时候能获得锁。
*/
//worker主要是对进行中的任务进行中断控制,顺带着对其他行为进行记录
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L; /** Thread this worker is running in. Null if factory fails. */
//正在跑的线程,如果是null标识factory失败
final Thread thread;
/** Initial task to run. Possibly null. */
//初始化一个任务以运行
Runnable firstTask;
/** Per-thread task counter */
//每个线程计数
volatile long completedTasks; /**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
* 用给定的first task和从threadFactory创建
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
} /** Delegates main run loop to outer runWorker */
//主要调用了runWorker
public void run() {
runWorker(this);
} // Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
//锁方法 //
protected boolean isHeldExclusively() {
return getState() != 0;
} //尝试获取锁
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//尝试释放锁
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
} public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); } void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}

  ThreadPoolExcuter 中的Worker其实可以看作高级一点的线程。其中继承AbstractQueuedSynchronizer主要是为了实现锁控制。ThreadPoolExecutor会持有并管理Worker,在Worker中firstTask其实就是存放task的,而thread则是存放当前Worker本身的线程。

其中比较重要的就是run方法了,但这个方法其实又是去调用ThreadPoolExecutor里面的runWorker()方法,具体可以看下一节的介绍。

4.ctl介绍以及运行状态说明
  首先需要介绍线程池有五种运行状态:
RUNNING(状态值-1): 接收新任务并处理队列中的任务
SHUTDOWN(状态值0): 不接收新任务但会处理队列中的任务。
STOP(状态值1): 不接收新任务,不处理队列中的任务,并中断正在处理的任务
TIDYING(状态值2): 所有任务已终止,workerCount为0,处于TIDYING状态的线程将调用钩子方法terminated()。
TERMINATED(状态值3): terminated()方法完成。

  然后我们可以看看ThreadPoolExcuter中的ctl这个变量。
  ctl是ThreadPoolExcuter中比较有意思的一个实现,它是一个AtomicInteger,这里不对AtomicInteger多做讨论,只要知道可以把它看成有原子性的Integer就够了,其实它具有原子性的原理是使用了CAS的技术,这是一种乐观锁的具体实现。
  ThreadPoolExcuter是将两个内部值打包成一个值,即将workerCount和runState(运行状态)这两个值打包在一个ctl中,因为runState有5个值,需要3位,所以有3位表示
runState,而其他29位表示为workerCount。
  而运行时要获取其他数据时,只需要对ctl进行拆包即可。具体这部分代码如下:

    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
  //拆包ctl,分别获取runState和WorkerCount
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; }

5.拒绝策略

当执行器(Executor)处于终止状态,或者执行器在max threads和工作队列都是有界并且处于饱和的时候,新提交的任务会被拒绝。在任一情况下,执行的任务将调用RejectedExecutionHandler的方法rejectedExecution(Runnable, ThreadPoolExecutor)。有以下四种拒绝策略:

1.默认的是ThreadPoolExecutor.AbortPolicy,在这种策略下,处理器会在拒绝后抛出一个运行异常RejectedExecutionException。

2.在ThreadPoolExecutor.CallerRunsPolicy的策略下,线程会调用它直接的execute来运行这个任务。这种方式提供简单的反馈控制机制来减缓新任务提交的速度。

3.在ThreadPoolExecutor.DiscardPolicy策略下,无法执行的任务将被简单得删除掉。

4.在ThreadPoolExecutor.DiscardOldestPolicy策略下,如果executor没有处于终止状态,在工作队列头的任务将被删除,然后会重新执行(可能会再次失败,这会导致重复这个过程)。

总结:本篇初步介绍了ThreadPoolExcuter的基本原理,解决了什么问题。而后说明了ThreadPoolExcuter中的初始化参数,对其中的各个参数做初步介绍。再之后介绍ctl变量的作用,并初步介绍了任务提交失败后的拒绝策略。

ThreadPoolExcuter源码解析(一)的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  4. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  5. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  6. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  7. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  8. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  9. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

随机推荐

  1. [WinForm]动态显示本地目录图片与悬浮窗

    加载显示: if (File.Exists(@"D:\产品图片\" + item + ".jpg")) { //需要判断是否存在图片 Image img = I ...

  2. InfoQ访谈:Webkit和HTML5的现状和趋势

    原网址: http://www.infoq.com/cn/interviews/status-and-trends-of-webkit-and-html5 个人一些不成熟的见解,望讨论和指正. 节选 ...

  3. 如何实现 集群化/Session 复制-doc(cluster-howto.html)

    源文档链接: http://tomcat.apache.org/tomcat-6.0-doc/cluster-howto.html 翻译日期: 2014年3月19日 翻译人员: 铁锚 感受: Tomc ...

  4. 页面缓存js问题解决

     1.在jsp中加入头 <META HTTP-EQUIV="Pragma" CONTENT="no-cache">  <META HTTP ...

  5. eclipse中Debug简单记忆

    最左边:代码一步一步的走,进入函数也是一步一步的走: 最中间:在断点开始一步一步的走,遇到函数不会进入函数,而是直接跳过函数(但是把函数中的代码整体走完的): 最右边:断点开始一部迅速返回上一级函数调 ...

  6. iOS9 系统分享调用(UIActivityViewController)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/49615109 ...

  7. Stripe Compaction

    借鉴于LevelDB.Cassandra的Compaction方法,https://issues.apache.org/jira/browse/HBASE-7667 提出了Stripe Compact ...

  8. 史上最简单的C语言链表实现,没有之一

    #include <stdio.h> #include <string.h> #include <stdlib.h> #define NR(x) (sizeof(x ...

  9. javac编译同一个包内的java文件

    问题描述:包a.b.c里有d.java e.java f.java三个文件,其中d中包含main. 错误: 第一种:javac d.java 报错:里面用到其他类,找不到 第二种:javac *.ja ...

  10. iPhone开发初探

    本文是作者从一无所知到入门的知识学习过程,并结合自己在嵌入式开发的经验所写的技术总结文章,以供后来者学习. 苹果公司的iphone平台采用Object-c做为native language的开发,Ob ...