有人可以解释为什么这个程序打印多个“中断”的陈述。
根据Thread.sleep javadoc
InterruptedException - 如果有任何线程中断了当前线程。抛出此异常时,将清除当前线程的中断状态。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) throws Exception {
for (int z = 0; z < 100; z++) {
ExecutorService executor1 = Executors.newSingleThreadExecutor();
executor1.execute(new Runnable() {
@Override
public void run() {
ExecutorService executor2 = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
executor2.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000 * 100);
} catch (InterruptedException e) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("interrupted");
}
}
}
});
}
executor2.shutdownNow();
try {
executor2.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
}
}
});
executor1.shutdown();
try {
executor1.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
}
}
}
}
答案 0 :(得分:2)
来自doc
如果在调用wait()时阻塞了这个线程,请等待(long), 或者等待(long,int)Object类或join()的方法, join(long),join(long,int),sleep(long)或sleep(long,int)方法 这个类,然后它的中断状态将被清除 收到InterruptedException。
在Thread.sleep(1000 * 100)
期间关闭执行程序服务并且InterruptedException
被抛出时,状态将被清除。
但ThreadPoolExecutor通过t.interrupt()
重新设置状态,因此您仍然可以获得状态。
以下是重置状态的一部分代码:
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
答案 1 :(得分:1)
这个答案是错的(保持原样,以便人们知道这个代码片段不是原因)
Executors.newFixedThreadPool()
方法返回ThreadPoolExecutor
如果你看ThreadPoolExecutor.shutdownNow()
,你会发现它可能会两次打断工人 -
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers(); //First interrupt here
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate(); //Second interrupt here
return tasks;
}
所以必须有些线程被中断两次。
他们第一次被打断,他们会从睡眠状态出来并抛出InterruptedException。但是在他们这样做之后,他们又被打断了(在执行你的代码之前)
答案 2 :(得分:1)
简而言之:
您会看到此行为,因为此处有竞争条件。有一个字段ReentrantLock ThreadPoolExecutor::mainLock
。方法shutdownNow
由它保护,但启动工作线程不是。
深入挖掘:
看看ThreadPoolExecutor::execute(Runnable)
。这是片段:
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); //add the worker to worker queue and start the thread
}
现在,让我们来看看工人是如何开始的。这是runWorker
方法片段(有些评论是我的):
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted()) //Was not interrupted yet by the shutdownNow method,
//but the pool is being stopped
wt.interrupt(); //<-- Interrupt it before running.
//...
try {
task.run();
}
//...
在这种情况下,关闭执行程序并执行任务很有效。
更具体地说,工作线程在 开始执行任务之前 中断 工作线程是 被ExecutorService::shutdownNow
方法中断,但 后,启动了关机。
如果您运气不好,在条件
之前shutdownNow
方法正在更新中断状态
if (Thread.currentThread().isInterrupted())
是checke,但在调用Thread.sleep(1000 * 100);
后立即抛出(因为已经中断)。
UPD :我在调用System.out.println(Thread.currentThread().isInterrupted());
之前插入了Thread.sleep(1000 * 100);
,现在我无法重现案例。
我可以猜到,在调用Thread::sleep
之前,方法ExecutorService::shutdownNow
会中断所有线程。
注意: 可能存在相同行为的其他原因,但是根据我运行的测试来判断这个似乎是最有可能的。