java使用默认线程池踩过的坑(二)

原创 2015年07月08日 10:36:20

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

是的,一个线程不能够启动两次。那么它是怎么判断的呢?
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()方法,很容易猜出,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();
}
}

Openstack从入门到精通-(最新Pike版避免踩坑)

本课程详细地介绍了openstack的原理,部署过程,全部为现场搭环境一步步实战操作,部署小技巧等,帮助云计算学员入门openstack,实现生产环境的前期知识原理。
  • 2017年10月23日 15:57

一次Java线程池误用引发的血案和总结

这是一个十分严重的问题自从最近的某年某月某天起,线上服务开始变得不那么稳定。在高峰期,时常有几台机器的内存持续飙升,并且无法回收,导致服务不可用。例如GC时间采样曲线:和内存使用曲线:图中所示,18:...
  • xunzaosiyecao
  • xunzaosiyecao
  • 2018-02-04 15:59:02
  • 235

java.util.concurrent.ThreadPoolExecutor实现机制简介

java.util.concurrent.ThreadPoolExecutor实现机制简介前言最近面试的时候被问到『如何实现一个线程池』的问题。当时没回答上来。 今天看了一下JDK源码,大概了解了一...
  • hammertank
  • hammertank
  • 2016-06-29 12:49:58
  • 2600

Java多线程:线程池简介及线程池之坑

线程池简介自JDK1.5,Java吊炸天的并发包就提供线程池java.util.concurrent.ThreadPoolExecutor ,先来看看其各个字段的含义: corePoolSize 核...
  • feichenwangyalin
  • feichenwangyalin
  • 2016-04-19 20:48:23
  • 3725

Java-线程池异常信息的坑

Java-线程池异常信息的坑大家平时肯定用过线程池,不知道有没有踩过这个坑。 线程中的任务出现异常没有任何错误日志信息。 描述 应用启动是通过多线程来初始化的。 但是出现莫名其妙的问题,什么问题呢...
  • jtaizlx0102
  • jtaizlx0102
  • 2016-04-12 00:08:28
  • 2337

线程池的坑

线程池的坑
  • zhangxiao93
  • zhangxiao93
  • 2017-05-27 12:05:27
  • 595

线程池学习-【转】一段JAVA线程池的设置引发的血案

程老师原文地址:http://flychao88.iteye.com/blog/2228423原文如下: 应公司需求我们对一个项目进行了线上压力测试,结果发现,三台服务器一共只有59TPS,结果惨不忍...
  • bohu83
  • bohu83
  • 2016-04-11 18:24:32
  • 2954

<em>Java线程池</em>及观察者模式解决多线程意外死亡重启问题

<em>Java线程池</em>及观察者模式解决多线程意外死亡重启问题,附件含两个要运行代码!... <em>Java线程池</em>及观察者模式解决多线程意外死亡...16:48:31 有点坑,分数值 太高,不值得...
  • 2018年04月18日 00:00

Java多线程编程-(7)-使用线程池实现线程的复用和一些坑的避免

前几篇: Java多线程编程-(1)-线程安全和锁Synchronized概念 Java多线程编程-(2)-可重入锁以及Synchronized的其他基本特性 Java多线程编程-(3)-线程本...
  • u010870518
  • u010870518
  • 2017-10-16 19:20:20
  • 2101

超越线程池:Java并发并没有你想的那么糟糕

很多人一直唠叨着并发中的新概念。然而,许多开发人员还没有机会把过多的注意力都放在上面。在这篇文章中,我们将带您了解Java 8 streams、 Hadoop、 Apache Spark、 Quasa...
  • chenleixing
  • chenleixing
  • 2015-03-25 21:11:05
  • 1977
收藏助手
不良信息举报
您举报文章:java使用默认线程池踩过的坑(二)
举报原因:
原因补充:

(最多只允许输入30个字)