有人可以解释为什么这个程序打印多个"中断"语句吗?
根据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;
}
因此,一定是某些线程被中断了两次。
当他们第一次被打断时,他们会从睡眠状态中出来并抛出一个中断异常。但是在他们这样做之后,他们再次被打断(在执行你的代码之前)