CachedThreadPool里的线程是如何被回收的?
线程池创建线程的逻辑图:

我们分析CachedThreadPool线程池里的线程是如何被回收的。
//Executors
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
} //ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
牢牢记住CachedThreadPool的corePoolSize=0, maximumPoolSize=Integer.MAX_VALUE
工作线程的死循环:
//ThreadPoolExecutor
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
当工作线程第二次获取的task等于null时,线程将退出while循环,于是就死掉了。
//ThreadPoolExecutor
private Runnable getTask() {
// 标记poll()是否超时
boolean timedOut = false; retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c); // Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//工作线程数-1
decrementWorkerCount();
//返回null,工作线程将退出while循环,即线程会死掉
return null;
} boolean timed; // Are workers subject to culling? for (;;) {
int wc = workerCountOf(c);
// allowCoreThreadTimeOut 默认为 false, newCachedThreadPool的corePoolSize为0
// 所以 timed = false || true,timed恒为true
timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 对于newCachedThreadPool,wc 恒小于 maximumPoolSize
// 第一次进for循环 true && !(false && true) = true
// poll超时后,第二次进for循环 true && !(true && true) = false
if (wc <= maximumPoolSize && ! (timedOut && timed))
break;
if (compareAndDecrementWorkerCount(c))
//poll超时后,第二次进for循环,
return null;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
} try {
//走poll分支
//poll会阻塞,直到有人调用workQueue.offer;或者超时返回null
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
//超时
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
如图中所示:有2种情况会创建工作线程,1. 工作线程数小于corePoolSize;2. 入队失败,且工作线程数小于maximumPoolSize
//ThreadPoolExecutor
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException(); int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//对于CachedThreadPool,如果有工作线程在poll中阻塞,则入队成功
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);
}
//对于CachedThreadPool,如果没有工作线程在poll中阻塞,则入队失败
//初次调用execute,走这个分支,创建工作线程
else if (!addWorker(command, false))
reject(command);
}
CachedThreadPool使用的是SynchronousQueue的
入队 :offer(E e)
出队:poll(long timeout, TimeUnit unit)
工作线程调用poll阻塞,等待timeout时间,如果超时,则返回null并回收线程;如果在等待期内,有任务入队,则成功返回任务,继续执行线程while循环。
CachedThreadPool里的线程是如何被回收的?的更多相关文章
- Linux线程退出、资源回收、资源清理的方法
首先说明线程中要回收哪些资源,理解清楚了这点之后在思考资源回收的问题. 1.子线程创建时从父线程copy出来的栈内存; 线程退出有多种方式,如return,pthread_exit,pthread_c ...
- 小谈Java里的线程
今天,我们来谈一谈Java里的线程. 一.进程与线程的基本概念 大家可能没听过线程这个概念,但是相信,用计算机的朋友都听过进程这个概念.打开电脑的任务管理器,我们就可以看到许多进程.它们主要分为三类, ...
- Linux 系统编程 学习:09-线程:线程的创建、回收与取消
Linux 系统编程 学习:09-线程:线程的创建.回收与取消 背景 我们在此之前完成了 有关进程的学习.从这一讲开始我们学习线程. 完全的开发可以参考:<多线程编程指南> 在Linux ...
- 并发编程——认识java里的线程
本文系作者 chaoCode原创,转载请私信并在文章开头附带作者和原文地址链接. 违者,作者保留追究权利. 前言 并发编程在我们日常开发中是时时刻刻都有在用的,只不过大部分的代码底层已经帮我们去做了一 ...
- 简短总结一下C#里跨线程更新UI(转)
摘自: http://my.oschina.net/sdqxcxh/blog/53707 跨线程更新UI是写多线程程序尤其是通信类的程序经常遇到的问题,这里面主要的问题是冲突,比如数据线程想要更新UI ...
- 简短总结一下C#里跨线程更新UI
摘自: http://my.oschina.net/sdqxcxh/blog/53707 跨线程更新UI是写多线程程序尤其是通信类的程序经常遇到的问题,这里面主要的问题是冲突,比如数据线程想要更新UI ...
- 【原创】一个线程oom,进程里其他线程还能运行吗?
引言 这题是一个网友@大脸猫爱吃鱼给我的提问,出自今年校招美团三面的一个真题.大致如下 一个进程有3个线程,如果一个线程抛出oom,其他两个线程还能运行么? 先说一下答案,答案是还能运行 不瞒大家说, ...
- [并发并行]_[C/C++]_[C++标准库里的线程安全问题]
场景 1.写普通的程序时, 经常会使用cout来做输出, 每个进程只有一个控制台, 如果多线程调用cout时会出状况吗? 2.之所以研究cout会不会在并发下调用有问题, 是因为曾经有一个bug的崩溃 ...
- 一个线程oom,进程里其他线程还能运行吗?
线程之间互相不影响:守护线程生活周期相同 引言 这题是一个网友@大脸猫爱吃鱼给我的提问,出自今年校招美团三面的一个真题.大致如下 一个进程有3个线程,如果一个线程抛出oom,其他两个线程还能运行么? ...
随机推荐
- 03: JavaScript基础
目录: 参考W3school 1.1 变量 1.2 JavaScript中数据类型 1.3 JavaScript中的两种for循环 1.4 条件语句:if.switch.while 1.5 break ...
- Android 实践项目开发二
在地图开发中项目中,我这周主要完成的任务是和遇到的问题是以下几个方面. 1.在本次的项目中主要是利用百度地图的.jar包实现地图的定位与搜索功能,需要在百度地图开发中心网站取得 密钥,并下载相关.ja ...
- 某模拟题(USACO部分题+noip2005部分题)
题目描述 农场上有N(1 <= N <= 50,000)堆草,放在不同的地点上.FJ有一辆拖拉机,也在农场上.拖拉机和草堆都表示为二维平面上的整数坐标,坐标值在1..1000的范围内.拖拉 ...
- JAVA I/O(四)网络Socket和ServerSocket
<Thinking in Enterprise Java>中第一章描述了用Socket和Channel的网络编程,核心即为Socket和Channel,本文简单讲述Socket的应用. S ...
- mac下的一些操作
mac 下修改Hosts文件 : http://www.cnblogs.com/zhangqs008/p/3773623.html mac下装Tomcat服务器: 在苹果系统安装Tomcat:首先下载 ...
- Python3基础 函数 递归 阶乘与斐波那契数列
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- Go第二篇之基本语法总结
Go语言标识符 标识符以字母或下划线开头,大小写敏感 _是特殊标识符,用来忽略结果 Go语言变量的声明 变量(Variable)的功能是存储用户的数据.不同的逻辑有不同的对象类型,也就有不同的变量类型 ...
- PyCharm/IDEA 使用技巧总结
基本概念 IDEA 没有类似 Eclipse 的工作空间的概念(workspace),最大单元就是 Project.这里可以把 Project 理解为 Eclipse 中的 workspace.Mod ...
- [luogu 1070]道路游戏(NOIP2009T4)
题目链接 题解: 题目描述好长啊.... 大概就是设一下$f[i]$表示第i秒的最大价值 首先枚举时间,然后因为机器人这一秒无论在哪里都是有可能的,所以要枚举一下每个点,又因为最多走p秒所以再枚举一下 ...
- 【转载】可被路由的协议 & 路由协议 & 不可被路由的协议 的区别
原文地址:可被路由的协议 & 路由协议 & 不可被路由的协议 的区别 术语routed protocol(可被路由的协议)和routing protocol(路由协议)经常被混淆.可被 ...