云智慧(北京)科技有限公司 陈鑫

是的。一个线程不可以启动两次。那么它是怎么推断的呢?

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使用默认线程池踩过的坑(二)的更多相关文章

  1. java使用默认线程池踩过的坑(三)

    云智慧(北京)科技有限公司 陈鑫 重新启动线程池 TaskManager public class TaskManager implements Runnable { -.. public TaskM ...

  2. 不要使用Java Executors 提供的默认线程池

    线程池构造方法 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUni ...

  3. Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...

  4. Java四种线程池

    Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:20 ...

  5. java并发:线程池、饱和策略、定制、扩展

    一.序言 当我们需要使用线程的时候,我们可以新建一个线程,然后显式调用线程的start()方法,这样实现起来非常简便,但在某些场景下存在缺陷:如果需要同时执行多个任务(即并发的线程数量很多),频繁地创 ...

  6. Java多线程和线程池

    转自:http://blog.csdn.net/u013142781/article/details/51387749 1.为什么要使用线程池 在Java中,如果每个请求到达就创建一个新线程,开销是相 ...

  7. Java多线程之线程池详解

    前言 在认识线程池之前,我们需要使用线程就去创建一个线程,但是我们会发现有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因 ...

  8. Java四种线程池的学习与总结

    在Java开发中,有时遇到多线程的开发时,直接使用Thread操作,对程序的性能和维护上都是一个问题,使用Java提供的线程池来操作可以很好的解决问题. 一.new Thread的弊端 执行一个异步任 ...

  9. java多线程、线程池及Spring配置线程池详解

    1.java中为什么要使用多线程使用多线程,可以把一些大任务分解成多个小任务来执行,多个小任务之间互不影像,同时进行,这样,充分利用了cpu资源.2.java中简单的实现多线程的方式 继承Thread ...

随机推荐

  1. sass07 函数

    scsss @function double($width){ //自定义函数u @return $width * 2; } @function double($width){ //自定义函数u @i ...

  2. BZOJ2179: FFT快速傅立叶 & caioj1450:【快速傅里叶变换】大整数乘法

    [传送门:BZOJ2179&caioj1450] 简要题意: 给出两个超级大的整数,求出a*b 题解: Rose_max出的一道FFT例题,卡掉高精度 = =(没想到BZOJ也有) 只要把a和 ...

  3. js如何实现简繁体互转

    js如何实现简繁体互转 一.总结 一句话总结:其实无论是简体还是繁体,都是在显示端(前端),其实所有的我只用动js就好了,没必要动php. 当然,后端也可以做前端的事情,只是麻烦了点(要多通信两次,第 ...

  4. [JZOJ4274] [NOIP2015模拟10.28B组] 终章-剑之魂 解题报告(二进制)

    Description [背景介绍]古堡,暗鸦,斜阳,和深渊……等了三年,我独自一人,终于来到了这里……“终焉的试炼吗?就在这里吗?”我自言自语道.“终焉的试炼啊!就在这里啊!”我再一次自言自语道.“ ...

  5. POJ 3671 DP or 乱搞

    思路: 1.DP f[i][j]:前i个数 最后一个数是j的最小花费 f[i][j]=min(f[i][j],f[i-1][k]+(a[i]!=j));1<=k<=j 这种做法比较有普遍性 ...

  6. 机器学习规则:ML工程最佳实践----rules_of_ml section 2【翻译】

    作者:黄永刚 ML Phase II: 特征工程 第一阶段介绍了机器学习的一个周期,为学习系统获取训练数据,通过有趣的引导设计指标,创建一个服务框架.在有了一个完整系统之后,就进入了第一阶段. 第二阶 ...

  7. c++几种排序算法代码

    #include <iostream> #include <vector> using namespace std; //交换int void swap(int& a, ...

  8. 51Nod 不重叠的线段(贪心)

    X轴上有N条线段,每条线段有1个起点S和终点E.最多能够选出多少条互不重叠的线段.(注:起点或终点重叠,不算重叠). 例如:[1 5][2 3][3 6],可以选[2 3][3 6],这2条线段互不重 ...

  9. CentOS6.9下sftp配置和scp用法

    基于 ssh 的 sftp 服务相比 ftp 有更好的安全性(非明文帐号密码传输)和方便的权限管理(限制用户的活动目录). 1.如果只想让某些用户只能使用 sftp 操作文件, 而不能通过ssh进行服 ...

  10. HTML标签和文档结构

    HTML标签与文档结构 HTML作为一门标记语言,是通过各种各样的标签来标记网页内容的.我们学习HTML主要就是学习的HTML标签. 那什么是标签呢? #1.在HTML中规定标签使用英文的的尖括号即` ...