引发中断异常时,不会清除线程中断状态



有人可以解释为什么这个程序打印多个"中断"语句吗?

根据Thread.sleep javadoc

中断异常 - 如果任何线程中断了当前线程。引发此异常时,将清除当前线程的中断状态。

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) {
}
}
}
}

输出 - 屏幕截图

来自文档

如果此线程在调用 wait(), wait(long) 时被阻塞, 或 Object(或 join() 的 wait(long, int) 方法, join(long), join(long, int), sleep(long), or sleep(long, int), 方法 的此类,则其中断状态将被清除,它将 收到中断异常。

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;
}

简而言之:

您会看到此行为,因为您在此处存在争用条件。有一个现场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:我在呼叫Thread.sleep(1000 * 100);之前插入了System.out.println(Thread.currentThread().isInterrupted());,现在我无法重现该案例。

我可以猜到,现在在调用Thread::sleep方法之前ExecutorService::shutdownNow中断所有线程。

注意:相同的行为很可能还有其他原因,但从我运行的测试来看,这个似乎是最有可能的。

这个答案是错误的(保持原样,以便人们知道这个代码片段不是原因)

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;
}

因此,一定是某些线程被中断了两次。

当他们第一次被打断时,他们会从睡眠状态中出来并抛出一个中断异常。但是在他们这样做之后,他们再次被打断(在执行你的代码之前)

相关内容

  • 没有找到相关文章

最新更新