有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

引发InterruptedException时,java线程中断状态未清除

有人能解释一下为什么这个程序会打印多个“中断”语句吗

根据线程。睡眠javadoc

InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.

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

output - screenshot


共 (3) 个答案

  1. # 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())
    

    是check,但是在对Thread.sleep(1000 * 100);的调用之后立即抛出(因为已经被中断)

    UPD:我在调用Thread.sleep(1000 * 100);之前插入了System.out.println(Thread.currentThread().isInterrupted());,现在我无法重现该情况

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

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

  2. # 2 楼答案

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

    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。但在他们这么做之后,他们又被打断了(在执行代码之前)

  3. # 3 楼答案

    来自doc

    If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

    当您在Thread.sleep(1000 * 100)期间关闭executor服务并抛出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;
        }