概要

线程池的实现类是ThreadPoolExecutor类。本章,我们通过分析ThreadPoolExecutor类,来了解线程池的原理。

ThreadPoolExecutor数据结构

ThreadPoolExecutor的数据结构如下图所示:

各个数据在ThreadPoolExecutor.java中的定义如下:

// 阻塞队列。
private final BlockingQueue<Runnable> workQueue;
// 互斥锁
private final ReentrantLock mainLock = new ReentrantLock();
// 线程集合。一个Worker对应一个线程。
private final HashSet<Worker> workers = new HashSet<Worker>();
// “终止条件”,与“mainLock”绑定。
private final Condition termination = mainLock.newCondition();
// 线程池中线程数量曾经达到过的最大值。
private int largestPoolSize;
// 已完成任务数量
private long completedTaskCount;
// ThreadFactory对象,用于创建线程。
private volatile 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对应的线程池启动时,它会执行线程池中的任务;当执行完一个任务后,它会从线程池的阻塞队列中取出一个阻塞的任务来继续运行。
    wokers的作用是,线程池通过它实现了"允许多个线程同时运行"。

2. workQueue
    workQueue是BlockingQueue类型,即它是一个阻塞队列。当线程池中的线程数超过它的容量的时候,线程会进入阻塞队列进行阻塞等待。
    通过workQueue,线程池实现了阻塞功能。

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

4. corePoolSize和maximumPoolSize
    corePoolSize是"核心池大小",maximumPoolSize是"最大池大小"。它们的作用是调整"线程池中实际运行的线程的数量"。
    例如,当新任务提交给线程池时(通过execute方法)。
          -- 如果此时,线程池中运行的线程数量< corePoolSize,则创建新线程来处理请求。
          -- 如果此时,线程池中运行的线程数量> corePoolSize,但是却< maximumPoolSize;则仅当阻塞队列满时才创建新线程。
          如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心池大小和最大池大小的值是在创建线程池设置的;但是,也可以使用 setCorePoolSize(int) 和 setMaximumPoolSize(int) 进行动态更改。

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

6. allowCoreThreadTimeOut和keepAliveTime
    allowCoreThreadTimeOut表示是否允许"线程在空闲状态时,仍然能够存活";而keepAliveTime是当线程池处于空闲状态的时候,超过keepAliveTime时间之后,空闲的线程会被终止。

7. threadFactory
    threadFactory是ThreadFactory对象。它是一个线程工厂类,"线程池通过ThreadFactory创建线程"。

详细见《juc线程池原理(二)

8. handler
    handler是RejectedExecutionHandler类型。它是"线程池拒绝策略"的句柄,也就是说"当某任务添加到线程池中,而线程池拒绝该任务时,线程池会通过handler进行相应的处理"。

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

9、AtomicInteger ctl

AtomicInteger类型的ctl代表了ThreadPoolExecutor中的控制状态,它是一个复合类型的成员变量,是一个原子整数,借助高低位包装了两个概念:

(1)workerCount:线程池中当前活动的线程数量,占据ctl的低29位;

(2)runState:线程池运行状态,占据ctl的高3位,有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五种状态。

AtomicInteger ctl的定义如下:

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

先说下workerCount:线程池中当前活动的线程数量,它占据ctl的低29位,这样,每当活跃线程数增加或减少时,ctl直接做相应数目的增减即可,十分方便。而ThreadPoolExecutor中COUNT_BITS就代表了workerCount所占位数,定义如下:

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

在Java中,一个int占据32位,而COUNT_BITS的结果不言而喻,Integer大小32减去3,就是29;另外,既然workerCount代表了线程池中当前活动的线程数量,那么
它肯定有个上下限阈值,下限很明显就是0,上限呢?ThreadPoolExecutor中CAPACITY就代表了workerCount的上限,它是ThreadPoolExecutor中理论上的最大活跃线程数,其定义如下:

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

运算过程为1左移29位,也就是00000000 00000000 00000000 00000001 --> 001 0000 00000000 00000000 00000000,再减去1的话,就是 000 11111 11111111 11111111 11111111,前三位代表线程池运行状态runState,所以这里workerCount的理论最大值就应该是29个1,即536870911;

既然workerCount作为其中一个概念复合在AtomicInteger ctl中,那么ThreadPoolExecutor理应提供从AtomicInteger ctl中解析出workerCount的方法,如下:

private static int workerCountOf(int c)  { return c & CAPACITY; }

计算逻辑很简单,传入的c代表的是ctl的值,即高3位为线程池运行状态runState,低29位为线程池中当前活动的线程数量workerCount,将其与CAPACITY进行与操作&,也就是与000 11111 11111111 11111111 11111111进行与操作,c的前三位通过与000进行与操作,无论c前三位为何值,最终都会变成000,也就是舍弃前三位的值,而c的低29位与29个1进行与操作,c的低29位还是会保持原值,这样就从AtomicInteger ctl中解析出了workerCount的值。
接下来,我们再看下runState:线程池运行状态,它占据ctl的高3位,有RUNNING(111)、SHUTDOWN(000)、STOP(001)、TIDYING(010)、TERMINATED(011)五种状态。我们先分别解释下这五种状态:

(1)RUNNING:接受新任务,并处理队列任务

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

-1在Java底层是由32个1表示的,左移29位的话,即111 00000 00000000 00000000 00000000,也就是低29位全部为0,高3位全部为1的话,表示RUNNING状态,即-536870912;

(2)SHUTDOWN:不接受新任务,但会处理队列任务

    private static final int SHUTDOWN   =  0 << COUNT_BITS;

0在Java底层是由32个0表示的,无论左移多少位,还是32个0,即000 00000 00000000 00000000 00000000,也就是低29位全部为0,高3位全部为0的话,表示SHUTDOWN状态,即0;

(3)STOP:不接受新任务,不会处理队列任务,而且会中断正在处理过程中的任务

    private static final int STOP       =  1 << COUNT_BITS;

1在Java底层是由前面的31个0和1个1组成的,左移29位的话,即001 00000 00000000 00000000 00000000,也就是低29位全部为0,高3位为001的话,表示STOP状态,即536870912;

(4)TIDYING:所有的任务已结束,workerCount为0,线程过渡到TIDYING状态,将会执行terminated()钩子方法

    private static final int TIDYING    =  2 << COUNT_BITS;

tidy英 [ˈtaɪdi] 美 [ˈtaɪdi] 使整洁;弄整齐;使有条理;整理,收拾

2在Java底层是由前面的30个0和1个10组成的,左移29位的话,即010 00000 00000000 00000000 00000000,也就是低29位全部为0,高3位为010的话,表示TIDYING状态,即1073741824;

(5)TERMINATED:terminated()方法已经完成

    private static final int TERMINATED =  3 << COUNT_BITS;

2在Java底层是由前面的30个0和1个11组成的,左移29位的话,即011 00000 00000000 00000000 00000000,也就是低29位全部为0,高3位为011的话,表示TERMINATED状态,即1610612736;

由上面我们可以得知,运行状态的值按照RUNNING-->SHUTDOWN-->STOP-->TIDYING-->TERMINATED顺序值是递增的,这些值之间的数值顺序很重要。随着时间的推移,运行状态单调增加,但是不需要经过每个状态。那么,可能存在的线程池状态的转换是什么呢?如下:

(1)RUNNING -> SHUTDOWN:调用shutdownNow()方法后,或者线程池实现了finalize方法,在里面调用了shutdown方法,即隐式调用;

(2)(RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()方法后;

(3)SHUTDOWN -> TIDYING:线程池和队列均为空时;

(4)STOP -> TIDYING:线程池为空时;

(5)TIDYING -> TERMINATED:terminated()钩子方法完成时。

我们再来看下是实现获取运行状态的runStateOf()方法,代码如下:

  1. private static int runStateOf(int c)     { return c & ~CAPACITY; }  

~是按位取反的意思,CAPACITY表示的是高位的3个0,和低位的29个1,而~CAPACITY则表示高位的3个1,2低位的9个0,然后再与入参c执行按位与操作,即高3位保持原样,低29位全部设置为0,也就获取了线程池的运行状态runState。

最后,我们再看下原子变量ctl的初始化方法ctlOf(),代码如下:

private static int ctlOf(int rs, int wc) { return rs | wc; } 

很简单,传入的rs表示线程池运行状态runState,其是高3位有值,低29位全部为0的int,而wc则代表线程池中有效线程的数量workerCount,其为高3位全部为0,而低29位有值得int,将runState和workerCount做或操作|处理,即用runState的高3位,workerCount的低29位填充的数字,而默认传入的runState、workerCount分别为RUNNING和0。

参考:http://www.cnblogs.com/skywang12345/p/3509941.html

参考:http://blog.csdn.net/lipeng_bigdata/article/details/51232266

juc线程池原理(二):ThreadPoolExecutor的成员变量介绍的更多相关文章

  1. juc线程池原理(四): 线程池状态介绍

    <Thread之一:线程生命周期及五种状态> <juc线程池原理(四): 线程池状态介绍> 线程有5种状态:新建状态,就绪状态,运行状态,阻塞状态,死亡状态.线程池也有5种状态 ...

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

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

  3. juc线程池原理(三):ThreadFactory、拒绝策略、提交任务、关闭线程池

    概要 (一) ThreadFactory 线程池中的ThreadFactory是一个线程工厂,线程池创建线程都是通过线程工厂对象(threadFactory)来完成的. 类图如下: 上面所说的thre ...

  4. 线程池原理讲解——ThreadPoolExecutor

    [这是前几天的存货,留着没发表,今天又复习一遍,润化了部分内容,继续干] 说线程池前,先简单回顾一下线程的状态吧: 1.线程状态转换 线程的五种状态,及其转换关系: 2.线程创建方式 三种:两个接口一 ...

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

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

  6. juc线程池原理(五):拒绝策略示例

    概要 拒绝策略介绍 线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施.当任务添加到线程池中之所以被拒绝,可能是由于:第一,线程池异常关闭.第二,任务数量超过线程池的最大限制. 线程池 ...

  7. juc线程池原理(六):jdk线程池中的设计模式

    一.jdk中默认线程池中的代理模式 单例类线程池只有一个线程,无边界队列,适合cpu密集的运算.jdk中创建线程池是通过Executors类中提供的静态的方法来创建的,其中的单例类线程池的方法如下: ...

  8. juc线程池原理(一):总体介绍

    概要 线程池类图 线程池的类图如下: 1. Executor 它是"执行者"接口,它是来执行任务的.准确的说,Executor提供了execute()接口来执行已提交的 Runna ...

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

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

随机推荐

  1. scala学习手记6 - 字符串与多行原始字符串

    scala中的字符串类就是java中的java.lang.String类.不过scala也为String提供了一个富封装类:scala.runtime.RichString. scala可以将java ...

  2. 虚拟主机(多站点配置)的实现--centos上的实现

    Apache中配置多主机多站点,可以通过两种方式实现 将同一个域名的不同端口映射到不同的站点(虚拟主机) 将同一个端口映射成不同的域名,不同的域名映射到不同的站点 两种方法可以同时存在,局域网通过   ...

  3. python学习笔记(unittest)

    刚刚放假回来我想很多人都还没有缓过来吧 这次介绍一个python自带的测试框架 unitest #!/usr/bin/env python # -*- coding: utf_8 -*- import ...

  4. 跨平台TTS eSpeak Windows开发

    转摘请说明出处:http://www.cnblogs.com/luochengor/p/3511165.html以及作者,谢谢. eSpeak是最为流行的开源跨平台的文本转语音程序.这两天进行了简单的 ...

  5. poj1469

    题解: 二分图匹配 然后判断最大匹配是否是m 代码: #include<cstdio> #include<cmath> #include<algorithm> #i ...

  6. org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactory' defined in class path resource

    二月 20, 2017 3:09:47 下午 org.apache.catalina.startup.SetAllPropertiesRule begin警告: [SetAllPropertiesRu ...

  7. 2017.10.26 ECN + product spec+ cypress ble module test+

    1 ECN Ecn  should be issued when modifying drawing,Copy children BOM of subassembly from BIL if one ...

  8. forEach和map的区别

    写法上没什么区别,只是返回值会不一样,map能够返回每一项,而forEach则返回undefined,以后要用哪个你知道了吧?map返回新的数组,可以进行后续更多的操作,例如: let arr = [ ...

  9. WeChat on Web 部分功能模拟实现

    Flask from flask import Flask,request,render_template,session,jsonify import time import requests im ...

  10. 限流之令牌桶算法——RateLimiter官方文档

    原文链接 作者:Dimitris Andreou  译者:魏嘉鹏 校对:方腾飞 RateLimiter 从概念上来讲,速率限制器会在可配置的速率下分配许可证.如果必要的话,每个acquire() 会阻 ...