先来看ThreadPoolExecutor的execute方法,这个方法能体现出一个Task被加入到线程池之后都发生了什么:

    public void execute(Runnable command) {
if (command == null)
throw new NullPointerException(); /* 如果运行中的worker线程数少于设定的常驻线程数,增加worker线程,把task分配给新建的worker线程 */
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
} // 如果任务可以被加入到任务队列中,即等待的任务数还在允许的范围内,
// 再次检查线程池是否被关闭,如果关闭的话,则移除任务并拒绝该任务
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
} // 如果任务数超过了现有worker线程的承受范围,尝试新建worker线程
// 如果无法添加新的worker线程,则会拒绝该任务
else if (!addWorker(command, false))
reject(command);
}

在执行任务时,需要经常检查线程池的状态,那么接下来说说线程池是如何进行状态控制的。上面的代码有个成员变量叫做ctl,它用于标记线程池状态和worker线程的数量,是一个AutomaticInteger对象。

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

ctl是一个32位的整数,最高的3位表示状态:

111为running,

000为shutdown,

001为stop,

010为tidying,

011为ternimated。

因此状态值就是这三位加上29个0,因此running的值是个负整数(最高位为1),其他状态都是正整数,后面判断状态会比较值的大小时会用到这点。

剩下的29位表示worker线程的数量(因此最大允许的线程数就是2的29方减1)。

这里是说说这几个状态的意义,这几个状态发生的顺序正好就是上面列出的顺序:

running表示正常运行状态

shutdown状态意味着发出了一个shutdown信号,类似于你点击了windows的关机按钮

stop表示shutdown信号收到,等于windows响应了这个信号,发出正在关机的信息

tidying发生在stop之后做出的响应,表示这个时候在清理一些资源,

ternimated发生在tidying完成之后,表示关闭完成。

接着来看添加一个worker线程时都发生了什么:

    private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // 返回false的情况:
// 1. rs>shutdown,即shutdown和running以外的状态
// 2. shutdown的状态
// 1)firstTask不为null,即有task分配
// 2)没有task,但是workQueue(等待任务队列)为空
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false; for (;;) {
// 1. 如果没有设定线程数的限制,worker线程数不能大于最大值(2的29次方-1)
// 2. 如果是固定尺寸的线程池,不能大于固定尺寸
// 3. 如果是可扩展的线程池,不能大于规定的线程数的上限
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 用CAS操作增加线程数量,如果失败,重新循环
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
loop
}
} // 新建worker线程
Worker w = new Worker(firstTask);
Thread t = w.thread; final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int c = ctl.get();
int rs = runStateOf(c); // 检查以下任一状态是否出现:
// 1. 创建线程失败
// 2. rs>shutdown,即shutdown和running以外的状态
// 3. rs==shutdown,有任务分配
if (t == null ||
(rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null))) {
decrementWorkerCount();
tryTerminate();
return false;
} workers.add(w); int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
} finally {
mainLock.unlock();
} t.start();
// 这里考虑一种极少出现的情况,如果worker线程调用start没有完成时,
// 线程池进入Stop状态,这个时候会调用Thread#interrupt中断每个
// worker线程,但是 interrupt对没有start的线程不一定起作用,这样
// 就会漏掉了对这个thread的interrupt,因此在worker线程start之后
// 检查以下,如果stop了,而这个线程却没有被interrupt,补上这个漏掉
// 的interrupt。
if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted())
t.interrupt(); return true;
}

这篇文章主要讲线程池如何处理任务,下一篇文章将会讲worker线程是如何工作的。

《java.util.concurrent 包源码阅读》11 线程池系列之ThreadPoolExecutor 第一部分的更多相关文章

  1. 《java.util.concurrent 包源码阅读》 结束语

    <java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...

  2. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  3. 《java.util.concurrent 包源码阅读》04 ConcurrentMap

    Java集合框架中的Map类型的数据结构是非线程安全,在多线程环境中使用时需要手动进行线程同步.因此在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:Concu ...

  4. 《java.util.concurrent 包源码阅读》02 关于java.util.concurrent.atomic包

    Aomic数据类型有四种类型:AomicBoolean, AomicInteger, AomicLong, 和AomicReferrence(针对Object的)以及它们的数组类型, 还有一个特殊的A ...

  5. 《java.util.concurrent 包源码阅读》17 信号量 Semaphore

    学过操作系统的朋友都知道信号量,在java.util.concurrent包中也有一个关于信号量的实现:Semaphore. 从代码实现的角度来说,信号量与锁很类似,可以看成是一个有限的共享锁,即只能 ...

  6. 《java.util.concurrent 包源码阅读》06 ArrayBlockingQueue

    对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现.所以分析ArrayBlockingQueue也是基于这两点. 对于线程安全来说,所有的添加元素的方法和拿走元 ...

  7. 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇

    concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...

  8. 《java.util.concurrent 包源码阅读》22 Fork/Join框架的初体验

    JDK7引入了Fork/Join框架,所谓Fork/Join框架,个人解释:Fork分解任务成独立的子任务,用多线程去执行这些子任务,Join合并子任务的结果.这样就能使用多线程的方式来执行一个任务. ...

  9. 《java.util.concurrent 包源码阅读》24 Fork/Join框架之Work-Stealing

    仔细看了Doug Lea的那篇文章:A Java Fork/Join Framework 中关于Work-Stealing的部分,下面列出该算法的要点(基本是原文的翻译): 1. 每个Worker线程 ...

随机推荐

  1. inotify-tools + php脚本实现Linux服务器文件监控并邮件提醒

      需求简介: 由于服务器被挂马,经常被写入涉敏感的html网页,领导时常被网监请去喝茶,呵呵你懂的.所以有两个需求,一是找出服务器的木马后门和修复代码漏洞,二是监控服务器涉及增删改查的文件. 第一个 ...

  2. 【转】S3C2440与SDRAM NorFlash NandFlash连线分析

    一.SDRAM(HY57V561620F)连线分析 1.  S3C2440 有27根地址线ADDR[26:0],8根片选信号ngcs0-ngcs7,对应bank0-bank7,当访问bankx 的地址 ...

  3. 接口测试思路,jmeter,接口测试流程

    接口测试总结 一:接口测试思想 接口测试:通过向服务器端发送请求,获取响应与预期结果做对比的一种服务端黑盒测试过程. 解释:接口就是将浏览器,客户端,手机端,或者服务器调用另一个服务器的请求抽离出来测 ...

  4. sharepoint 创建个人网站

    One of the SharePoint 2013 puzzle pieces which got some major improvements are My Sites, User Profil ...

  5. 关于EF Code First模式不同建模方式对建表产生的影响

    今天在学EF Code First模式的时候,发现几个很有趣的问题,问题如下: 1.当编写玩实体后,不指定任何主键约束,EF会找长的最像Id的,然后设置其为主键,验证代码如下: //User类 cla ...

  6. 超文本传送协议HTTP

    1. HTTP的操作过程: HTTP是面向事务的应用层协议.HTTP协议本身是无连接的,为了保证数据的可靠传输,HTTP使用了面向连接的TCP作为运输层协议.所以,在发送HTTP报文之前都需要先建立T ...

  7. iOS下OpenCV开发配置的两个常见问题(sign和link)

    本文为作者原创,转载请注明出处(http://www.cnblogs.com/mar-q/)by 负赑屃 先上可以运行官方推荐的<OpenCV for iOS samples>的demo链 ...

  8. AsyncTask学习

    在学习Android的时候,开始用到比较多的异步处理的类大概就是AsyncTask,但是我们很多时候只知道调用,却不知道思考一些东西. 本文就简单的总结和分析了一些AsyncTask的知识. 一.As ...

  9. Spring容器组建注解@Component和Resouces实现完全注解配置

    @Resource和@Component实现零XML配置 1.@Resource的注解: @Resource是J2EE的注解.意思是说在容器里面找相应的资源.也可以通过name属性指定它name的资源 ...

  10. MicroService.Core简易微服务框架《一、简介》

    MicroService.Core MicroService.Core 的初衷是为了方便的创建一个微服务, 可作为 Windows Service 或者控制台模式启动. 它底层使用了 OWin 自托管 ...