Java线程池实现类ThreadPoolExecutor源码分析

线程池实现类ThreadPoolExecutor是在java.util.concurrent下的,从JDK1.5开始支持线程池实现类ThreadPoolExecutor.

该类有四个构造函数(不含无参构造函数),分别为:

// 常用的是这种,使用默认的线程工厂和拒绝策略

1、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue)

// 用户自定义线程工厂

2、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory)

// 用户自定义线程池饱和/执行拒绝策略

3、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,

RejectedExecutionHandler handler)

// 用户自定义线程工厂和线程池饱和/执行拒绝策略

4、ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

其中:1、2、3构造函数均调用this(args...)构造函数,即构造函数4。

Executors类提供了默认的ThreadFactory(线程工厂),即Executors.defaultThreadFactory(),

ThreadPoolExecutor提供了默认的线程池饱和和执行拒绝策略,private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();

线程池实现类的构造函数参数:

corePoolSize(线程池核心线程大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,当任务数大于核心线程数的时候就不会再创建。在这里要注意一点,线程池刚创建的时候,其中并没有创建任何线程,而是等任务来才去创建线程,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法 ,这样才会预先创建好corePoolSize个线程或者一个线程

maximumPoolSize(线程池最大线程数):线程池允许创建的最大线程数,如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如果使用了无界队列,此参数就没有意义了

keepAliveTime(线程活动保持时间):此参数默认在线程数大于corePoolSize的情况下才会起作用, 当线程(默认是针对非核心线程)的空闲时间达到keepAliveTime的时候就会终止,直至线程数目小于corePoolSize。不过如果调用allowCoreThreadTimeOut方法,则当线程数目小于corePoolSize的时候也会起作用.

unit(keelAliveTime的时间单位):keelAliveTime的时间单位,一共有7种,在这里就不列举了。

workQueue(阻塞队列):阻塞队列,用来存储等待执行的任务,这个参数也是非常重要的,在这里简单介绍一下几个阻塞队列。

    1)ArrayBlockingQueue:这是一个基于数组结构的有界阻塞队列,此队列按照FIFO的原则对元素进行排序。

    2)LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按照FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()就是使用了这个队列。

    3)SynchronousQueue:一个不存储元素的阻塞队列每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool()就使用了这个队列。

    4)PriorityBlockingQueue一个具有优先级的无阻塞队列

handler(饱和策略);当线程池和队列都满了,说明线程池已经处于饱和状态了,那么必须采取一种策略来处理还在提交过来的新任务。这个饱和策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。共有四种饱和策略提供,当然我们也可以选择自己实现饱和策略。

AbortPolicy:直接丢弃并且抛出RejectedExecutionException异常,这种是默认的饱和策略

CallerRunsPolicy:只用调用者所在线程来运行任务,如果线程池处于Running状态,直接在提交任务的线程中执行任务;如果线程池不处于Running状态,直接丢弃任务。

DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务

DiscardPolicy:丢弃任务并且不抛出异常,如果线程池处于Running状态,从阻塞队列中移除头部任务并重新调用execute方法

ThreadPoolExecutor类中的几个关键属性及方法源码解析:

查看ThreadPoolExecutor源码,可以发现,有以下属性:

//ctl是线程池主要的控制状态,是一个复合类型的变量,其中包括了两个概念。

// ctl的高3位代表线程池的状态,低29位代表workerCount

// workerCount:表示有效的线程数目(低29位)

// runState:线程池里线程的运行状态(高3位)

// 线程池的控制状态主要分为:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATE

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

// COUNT_BITS用来表示线程池数量的位数,Integer.SIZE=32,因此COUNT_BITS=29,减3是为线程池运行状态预留3位bit位。

private static final int COUNT_BITS = Integer.SIZE - 3; 

// 线程池最大线程数量,线程池最大线程数量为2^29-1个

private static final int CAPACITY = (1 << COUNT_BITS) - 1; 

// runState is stored in the high-order bits,线程池运行状态,存储在ctl变量的高位(高3位)

// 高3位为111,则是在RUNNING状态,

private static final int RUNNING = -1 << COUNT_BITS; 

// 高3位位000,则是在SHUTDOWN状态

private static final int SHUTDOWN = 0 << COUNT_BITS;      // 0 

// 高3位为001,则是在STOP状态

private static final int STOP = 1 << COUNT_BITS; 

// 高3位为010,则是在TIDYING状态

private static final int TIDYING = 2 << COUNT_BITS; 

// 高3位为110,则是在TERMINATED状态

private static final int TERMINATED = 3 << COUNT_BITS; 

// Packing and unpacking ctl 

// 获取当前运行状态,通过对CAPACITY取反,然后和传进来的参数进行与运算

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

// 阻塞队列

private final BlockingQueue workQueue;

// 可重入锁,访问workers集合、访问largestPoolSize等线程池的统计型变量、执行shutdown方法、执行shutdownNow方法都需要获取该锁

private final ReentrantLock mainLock = new ReentrantLock();

// 线程池内的有效线程

private final HashSet<Worker> workers = new HashSet<Worker>();

//和mainLock绑定的Condition对象,执行awaitTermination方法和tryTerminate方法时使用该Condition对象

private final Condition termination = mainLock.newCondition();

// 线程工厂

private volatile ThreadFactory threadFactory;

// 线程池达到饱和后的拒绝策略

private volatile RejectedExecutionHandler handler;

线程池的控制(运行)状态

线程池的控制状态主要分为:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATE

每种控制状态下,对新创建的线程(任务)和已在阻塞队列的线程(任务)的策略都有不同,

1)RUNNING状态:线程池可以接受新任务并且处理已经在阻塞队列的任务。

2)SHUTDOWN状态: 线程池不接受新任务,但是处理已经在阻塞队列的任务。

3)STOP状态: 线程池不接受新任务,也不处理阻塞队列里的任务并且会中断正在处理的任务。

4)TIDYING状态:线程池所有任务都被中止,workerCount(有效线程数量)是0,线程状态转化为TIDYING并且调用terminated()钩子方法。

5)TERMINATE状态:terminated()钩子方法已经完成。

线程池执行线程(调用execute(Runnable command)方法)流程:

1、首先判断任务是否为空,空则抛出空指针异常(if command==null throw new NullPointerException();)

2、command不为空则获取线程池控制状态ctl值(包括:运行状态和有效线程数),判断有效线程数是否小于corePoolSize,小于添加到worker集合当中执行,如成功,则返回(执行线程command任务),失败的话再接着获取线程池控制状态,因为只有状态变了才会失败,所以重新获取。

3、判断线程池是否处于运行状态是的话则添加command到阻塞队列,加入时也会再次获取状态并且检测

​ 状态是否不处于运行状态,不处于的话则将command从阻塞队列移除,并且拒绝任务

4、如果线程池里没有了线程(workerCountOf 方法返回值为0),则创建新的线程去执行获取阻塞队列的任务执行。

5、如果以上都没执行成功,则需要开启最大线程池里的线程来执行任务,失败的话就丢弃。

线程池执行线程的流程图例,(借用作者:KingJack文章配图):


线程池执行线程流程
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容