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,其他两个线程还能运行么? ...
随机推荐
- Centos下安装git高版本2.1.2
安装依赖软件 # yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel asciidoc # yum in ...
- 面向对象初调用:foolish 电梯
本周我们完成的任务是傻瓜电梯的调度,对于那十分十分详细的指导书,我感觉想要说明白题目要求,是做不到的,所以就把指导书贴出来给大家看了,,由于在下还不会网页制作,只能通过百度网盘了,https://pa ...
- GNU C 中零长度的数组【转】
原文链接:http://www.cnblogs.com/dolphin0520/p/3752492.html 在标准C和C++中,长度为0的数组是被禁止使用的.不过在GNU C中,存在一个非常奇怪的用 ...
- luoguP2572 [SCOI2010]序列操作
题目&&链接 反正数据都是一样的,luogu比较友好 luogu bzoj lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变 ...
- C#中的异步编程模式
异步编程模型(APM) 基于事件的异步编程模式 基于任务的异步模式 Async Await编程 关于C#,可以看看Learning Hard的博客
- 【第三十五章】 metrics(3)- codahale-metrics基本使用
<!-- metrics --> <dependency> <groupId>io.dropwizard.metrics</groupId> <a ...
- The way to Go(4): Go runtime及解释器
Reference: Github: Go Github: The way to Go Go runtime Go runtime: 尽管 Go 编译器产生的是本地可执行代码,这些代码仍旧运行在 Go ...
- 【TCP/IP详解 卷一:协议】TCP定时器 小结
前言 在有关TCP的章节中,介绍了四种定时器,它们体现了TCP的可靠性,其中最重要的 就是重传定时器了,剩下的定时器都是为了解决TCP的理解上的一些问题而设置的. 四种定时器: 2MSL定时器,出现在 ...
- LCA在线算法详解
LCA(最近公共祖先)的求法有多种,这里先介绍第一种:在线算法. 声明一下:下面的内容参考了http://www.cnblogs.com/scau20110726/archive/2013/05/26 ...
- GATK 一些资料
1. http://blog.sciencenet.cn/home.php?mod=space&uid=1469385&do=blog&classid=166694&v ...