3

我有一些使用Java中断机制来完成我的工作的经验,但是我目前还不清楚什么时候应该设置当前线程的中断状态,什么时候应该抛出InterruptedException?

而且,为了让您更清楚,这是我之前编写的示例。

这是我开始工作之前的代码:

/*
 * Run a command which locates on a certain remote machine, with the
 * specified timeout in milliseconds.
 * 
 * This method will be invoked by means of
 *     java.util.concurrent.FutureTask
 * which will further submmited to a dedicated
 *     java.util.concurrent.ExecutorService
 */
public void runRemoteSript(String command, long timeout) {
    Process cmd = null;
    try {
        cmd = Runtime.getRuntime().exec(command);

        boolean returnCodeOk = false;
        long endTime = System.currentTimeMillis() + timeout;

        // wait for the command to complete
        while (!returnCodeOk && System.currentTimeMillis() < endTime) {

            // do something with the stdout stream
            // do something with the err stream

            try {
                cmd.exitValue();
                returnCodeOk = true;
            } catch (IllegalThreadStateException e) { // still running
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ie) {
                    // The original code just swallow this exception
                }
            }
        }

    } finall {
        if (null != cmd) {
            cmd.destroy();
        }
    }
}

我的意图是中断命令,因为一些远程脚本在完成之前会消耗大量时间。因此 runRemoteScript 可以完成或手动停止。这是更新的代码:

public void cancel(String cmd) {
    // I record the task that I've previously submitted to
    // the ExecutorService.
    FutureTask task = getTaskByCmd(cmd);

    // This would interrupt the:
    //     Thread.sleep(200);
    // statement in the runRemoteScript method.
    task.cancel(true);
}

public void runRemoteSript(String command, long timeout) {
    Process cmd = null;
    try {
        cmd = Runtime.getRuntime().exec(command);

        boolean returnCodeOk = false;
        long endTime = System.currentTimeMillis() + timeout;

        // wait for the command to complete
        **boolean hasInterruption = false;**
        while (!returnCodeOk && System.currentTimeMillis() < endTime) {

            // do something with the stdout stream
            // do something with the err stream

            try {
                cmd.exitValue();
                returnCodeOk = true;
            } catch (IllegalThreadStateException e) { // still running
                try {
                    Thread.sleep(200);
                } catch (InterruptedException ie) {
                    // The updated code comes here:
                    hasInterruption = true; // The reason why I didn't break the while-loop
                                            // directly is: there would be a file lock on
                                            // the remote machine when it is running, which
                                            // will further keep subsequent running the same
                                            // script. Thus the script will still running
                                            // there.
                }
            }
        }

        // let the running thread of this method have the opportunity to
        // test the interrupt status
        if (hasInterruption) {
            Thread.currentThread().interrupt();
        }

        // Will it be better if I throws a InterruptedException here?

    } finall {
        if (null != cmd) {
            cmd.destroy();
        }
    }
}

关键是,设置调用线程测试的中断状态更好吗?或者只是向调用者抛出一个新的 InterrutedExeption是否有任何最佳实践或特定情况更适合上述方法之一?

我会在这里写下我的理解,如果我误解了其中任何一个,您可以纠正我的理解。根据我的理解,我认为 thread.interrupt() 旨在不要求客户端代码处理中断,并且由客户端代码负责决定是否对其进行测试,而 throws InterruptedException 是必须的,因为它是检查异常?

4

4 回答 4

6

See this other answer, which links to a very good discussion of interrupts. The main idea is that you should throw the InterruptedException, except where it is impossible. If it's impossible, you should reset the interrupted status by calling Thread.currentThread().interrupt().

Why might it be impossible to rethrow the interrupted exception? You might be calling Thread.sleep() inside the run() method of a class that implements Runnable (which it doesn't look like you are). Because InterruptedException is checked and the run method is not declared to throw any exceptions in the Runnable interface, you cannot rethrow the exception. In that case, it is best to reset the interrupted status, and many containers that use Runnable will handle that properly.

In any case, you're doing the right thing by changing the code so it doesn't swallow the exception.

于 2013-11-13T04:54:19.383 回答
1

中断一个线程是一种告诉这个线程尽早完成执行的机制,如果可能的话放弃当前的任务。通常调用它来终止线程,并且您的代码应该正确地尊重它。这可以通过使用全局try/catch 块来完成,以便线程立即跳出任何 while 循环(如果有)。

如果您使用阻塞代码,您可以假设内部代码最终会在正确的时刻抛出 InterruptedException。如果你只是有一个长的计算,你应该经常检查中断。

阻塞代码的正确实现应该如下所示:

public void run() {
  try {
    while (this.running) {
      doSomethingThatBlocks();
    }
  } catch (InterruptedException e) {
    // maybe log if this wasn't expected
  } finally {
    cleanup();
  }
}

非阻塞代码也是如此,你必须自己检查中断,应该如下所示:

public void run() {
  while (this.isInterrupted() == false) {
    calculation.nextStep();
  }
}

如果您正在设计阻塞 API 调用,则只需要实际抛出 InterruptedException。但是由于几乎所有可能阻塞的 Java 函数在必要时都已经抛出了一个 InterruptedException,因此不得不自己抛出一个通常是一种极端情况。

如果您确实有一个围绕现有 API 调用的包装器,并且需要拦截中断以进行一些清理,则只需在完成后抛出相同的中断即可。中断并不会伤害任何人,它毕竟只是一个终止信号。

于 2013-11-13T13:39:59.410 回答
0

何时使用 thread.interrupt()

每当您需要中断线程时,这不应该经常发生。

以及何时抛出 InterruptedException

只有当你需要抓住并重新抛出它时,这几乎是不可能的。

请注意,您选择了一个非常糟糕的示例。您应该只调用Process.waitFor(),并在返回时获取退出值(如果需要)。

于 2013-11-13T05:06:51.090 回答
0

你应该回到基本的——你的中断是为了什么?什么时候发生?什么是预期结果?在正常情况下,一个线程永远不会被自己以外的其他人中断。

您想取消正在进行的操作吗?然后抛出它或返回线程。你想消耗中断还是保留被调用模块?

在大多数情况下,您可以取消操作并退出。

于 2013-11-13T04:59:32.430 回答