如何在等待线程时优雅地降级程序?

时间:2011-12-21 00:55:47

标签: java multithreading concurrency graceful-degradation

我知道正常终止线程的构造:

public class Foo implements Runnable {
   private volatile boolean stop = false;

   public void stop() {
      stop = true;
   }

   public void run() {
      while (!stop) {
         ...
      }
   }
}

但是如果某个线程正在等待某个东西,在某个对象内(使用wait(),没有时间限制),那么这个构造对于停止这个线程没有用,因为他已经超过了while循环,所以他会永远继续。

那么,停止等待线程的正确方法是什么?

4 个答案:

答案 0 :(得分:2)

如果您不希望线程无限期地等待,请不要将它们编码为首先执行此操作。你正在编写他们的代码,所以写它来做你真正的事情所以你不必尝试从外面修复它。

答案 1 :(得分:2)

如果线程实际等待某事,你应该调用方法Thread.interrupt()来中断线程。不要在while循环中检查自定义变量,而是使用Thread.isInterrupted()或Thread.interrupted() - 一个重置中断标志,另一个不重置。

如果你在等待什么,我认为你必须抓住InterruptedException,不是吗?

答案 2 :(得分:1)

每个行为良好的阻塞方法都会声明一个已检查的异常InterruptedException,它实现了这个目的:通知线程在被阻塞时被中断。

您必须捕获此异常,实际上这可能会替换您的stop字段。

例如,让我们考虑一个日志系统,它将消息写入专用线程上的文件(这样在IO上花费的时间不会干扰您的应用程序 - 假设它不是IO重)。

每个线程都有一个 initerrupted flag ,可以通过Thread.currentThread().isInterrupted()读取。你尝试这样的事情:

class Logger {
  private final File file = ...;
  private final BlockingQueue<String> logLines = new LinkedBlockingQueue<String>();
  private final Thread loggingThread = new Thread(new Runnable(){
    @Override public void run() {
      PrintWriter pw;
      try {
        pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(file)));
        while (!Thread.currentThread().isInterrupted()) {
          try {
            pw.println(logLines.take());
          } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // good habit: make sure the interrupt status is set
          }
        }
        pw.flush();
        pw.close();
      } catch (IOException e) { ... flush and close pw if not null and open ... }
    }
  });
  { loggingThread.start(); }
  public void log(final String line) { logLines.offer(line); } // will always work, because logLines is a LinkedBQ.
  public void stop() { loggingThread.interrupt(); }
}

最后,为了正常关闭应用程序,必须确保在让JVM关闭之前终止此线程。要做到这一点,你必须绝对肯定在以任何可能的方式关闭之前调用stop(),或者你可以注册一个关闭钩子,通过在类中添加类似这个实例初始化器的东西:

class Logger {
  ...
  {
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
      @Override public void run() { close(); }
    }));
  }
}

这将强制JVM在终止之前调用close()(因此中断线程,刷新并关闭文件)。

答案 3 :(得分:0)

这一切都取决于你必须在你的线程中等待的原因。如果线程正在等待不间断IO,那么您可以查看Stop/Interrupt threads blocked on waiting input from socket

否则全部取决于你在线程中的等待方式。您可以使用wait(1000),然后检查标志并等待一些。您可以等待来自阻塞队列的消息,您可以使用锁/条件,即使wait/notify可以在这种情况下工作,您也需要正确处理中断。