云智慧(北京)科技有限公司 陈鑫
是的,一个线程不能够启动两次。那么它是怎么判断的呢?
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()) {
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();
for(worker w : workers)
w.interruptifstarted();
mainlock.unlock();