java使用默认线程池踩过的坑(二)
云智慧(北京)科技有限公司 陈鑫
是的。一个线程不可以启动两次。那么它是怎么推断的呢?
public synchronized void start() {
/**
* A zero status valuecorresponds to state “NEW”. 0相应的是state NEW
*/
if (threadStatus!= 0) //假设不是NEW state,就直接抛出异常!
throw newIllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0(); // 启动线程的native方法
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch(Throwable ignore) {
}
}
}
恩,仅仅有是NEW状态才可以调用native方法启动一个线程。好吧。到这里了。就普及也自补一下jvm里的线程状态:
全部的线程状态::
l NEW —— 还没有启动过
l RUNNABLE —— 正在jvm上执行着
l BLOCKED —— 正在等待锁/信号量被释放
l WAITING —— 等待其它某个线程的某个特定动作
l TIMED_WAITING —— A thread that iswaiting for another thread to perform an action for up to a specified waitingtime is in this state.
l TERMINATED —— 退出,停止
线程在某个时间点上仅仅可能存在一种状态。这些状态是jvm里的,并不反映操作系统线程的状态。查一下Thread的API,没有对其状态进行改动的API。那么这条路是不通的吗?
细致考虑一下……
假设把任务做成Runnable实现类,然后在把这个实现类丢进线程池调度器之前,利用此Runnable构造一个Thread,是不是这个Thread对象就行控制这个runnable对象。进而控制在线程池中执行着的task了呢?非也!让我们看看Thread和ThreadPoolExecutor对Runnable的处理吧。
Thread
/* What will berun. */
private Runnabletarget;
结合上面的start()方法,非常easy猜出,start0()会把target弄成一个线程来进行执行。
ThreadPoolExecutor
public void execute(Runnable command){
if (command== null)
thrownew NullPointerException();
int c =ctl.get();
if(workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c =ctl.get();
}
if(isRunning(c) && workQueue.offer(command)) {
intrecheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if(workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(RunnablefirstTask, boolean core) {
…
booleanworkerStarted = false;
booleanworkerAdded = false;
Worker w =null;
try {
finalReentrantLock mainLock = this.mainLock;
w = newWorker(firstTask);
finalThread t = w.thread;
if (t!= null) {
mainLock.lock();
try{
int c = ctl.get();
int rs = runStateOf(c);
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw newIllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize =s;
workerAdded = true;
}
}finally {
mainLock.unlock();
}
if(workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (!workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
那么Worker又是如何的呢?
Worker
private final class Worker
extendsAbstractQueuedSynchronizer
implementsRunnable
{
finalThread thread;
RunnablefirstTask;
volatilelong completedTasks;
Worker(Runnable firstTask) {
setState(-1); //调用runWorker之前不可以interrupt
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public voidrun() {
runWorker(this);
}
……
…….
voidinterruptIfStarted() {
Threadt;
if(getState() >= 0 && (t = thread) != null &&!t.isInterrupted()) {
try{
t.interrupt();
}catch (SecurityException ignore) {
}
}
}
}
可见worker里既包装了Runnable对象——task,又包装了一个Thread对象——以自己作为初始化參数。由于worker也是Runnable对象。
然后对外提供了执行与停止接口,run()和interruptIfStarted()。回想上面使用Thread的样例不禁有了新的领悟,我们把一个Thread对象交给ThreadPoolExecutor执行后。实际的调用是对Thread(FileTask())对象,我们临时称之为workerWrapper。
那么我们在池外进行FileTask.interrupt()操作影响的是FileTask对象。而不是workerWrapper。所以可能上面对于start()方法二次调用不是特别适当。更恰当的应该是在fileTask.interrupt()的时候就跑出异常,由于从来没有对fileTask对象执行过start()方法。这时候去interrupt就会出现错误。详细例如以下图:
分析到此,我们已经明白除了调用ThreadPoolExecutor了的interruptWorkers()方法别无其它途径操作这些worker了。
private void interruptWorkers() {
finalReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for(Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
java使用默认线程池踩过的坑(二)的更多相关文章
- java使用默认线程池踩过的坑(三)
云智慧(北京)科技有限公司 陈鑫 重新启动线程池 TaskManager public class TaskManager implements Runnable { -.. public TaskM ...
- 不要使用Java Executors 提供的默认线程池
线程池构造方法 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUni ...
- Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...
- Java四种线程池
Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:20 ...
- java并发:线程池、饱和策略、定制、扩展
一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...
- Java多线程和线程池
转自:http://blog.csdn.net/u013142781/article/details/51387749 1.为什么要使用线程池 在Java中,如果每个请求到达就创建一个新线程,开销是相 ...
- Java多线程之线程池详解
前言 在认识线程池之前,我们需要使用线程就去创建一个线程,但是我们会发现有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因 ...
- Java四种线程池的学习与总结
在Java开发中,有时遇到多线程的开发时,直接使用Thread操作,对程序的性能和维护上都是一个问题,使用Java提供的线程池来操作可以很好的解决问题. 一.new Thread的弊端 执行一个异步任 ...
- java多线程、线程池及Spring配置线程池详解
1.java中为什么要使用多线程使用多线程,可以把一些大任务分解成多个小任务来执行,多个小任务之间互不影像,同时进行,这样,充分利用了cpu资源.2.java中简单的实现多线程的方式 继承Thread ...
随机推荐
- angularjs作用域和函数调用
<!DOCTYPE HTML> <html ng-app> <head> <meta http-equiv="Content-Type" ...
- hpuoj--校赛--特殊的比赛日期(素数判断+模拟)
问题 B: 感恩节KK专场--特殊的比赛日期 时间限制: 1 Sec 内存限制: 128 MB 提交: 392 解决: 99 [提交][状态][讨论版] 题目描述 KK今天参加河南理工大学ACM程 ...
- django 笔记17 ModelForm
Model 数据库操作 验证 验证 Form -class LoginForm(Form): email = fields.EmailField() is_valid 每一个字段进行正则(字段内置正则 ...
- asp.net导出execl和图片
个人常用导出execl方法: #region "导出文档" /// <summary> /// Export("application/ms-excel&qu ...
- js中字符串下划线转为驼峰
function camelCase(string){ // Support: IE9-11+ return string.replace( /-([a-z])/g, function( all, l ...
- 对OC中property的一点理解
最近在看即将要加入的项目的代码,看到一个protocol里包含着几个property.之前没有写过类似的代码,看到这里的时候,突然疑惑了一下,发现自己对property的理解好像有点模糊.所以回家后又 ...
- subline Text3 插件安装
--没有解决,换了vscode 安装Package Control 这是必须的步骤,安装任何插件之前需要安装这个 自动安装的方法最方便,只需要在控制台(不是win的控制台,而是subline 的)里粘 ...
- 题解 BZOJ4919 【大根堆】
题面:传送门. 老师说今天要考一道线段树合并,然后...然后这道题我就GG了.(当然可以用线段树合并写,只是比较复杂) 有人赛时想了个贪心,然后被机房巨佬hack了,结果在hack的过程中巨佬想出了正 ...
- HTML学习----------DAY1 第三节
本章通过实例向您演示最常用的 HTML 标签. 提示:不要担心本章中您还没有学过的例子,您将在下面的章节中学到它们. 提示:学习 HTML 最好的方式就是边学边做实验.我们为您准备了很好的 HTML ...
- 洛谷 P3467 [POI2008]PLA-Postering
P3467 [POI2008]PLA-Postering 题目描述 All the buildings in the east district of Byteburg were built in a ...