Java 当有等待线程时,如何优雅地降级程序?

Java 当有等待线程时,如何优雅地降级程序?,java,multithreading,concurrency,graceful-degradation,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(),

我知道如何优雅地终止线程:

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

   public void stop() {
      stop = true;
   }

   public void run() {
      while (!stop) {
         ...
      }
   }
}
但是如果某个线程正在某个对象内等待某个东西(使用
wait()
,没有时间限制),那么此构造对于停止此线程将没有用处,因为他已经超过while循环中的条件,因此他将永远继续


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

如果您不希望线程无限期地等待,那么首先不要编写代码让它们这样做。您正在编写他们的代码,所以编写代码时要按照实际操作,这样您就不必尝试从外部修复代码。

这取决于您为什么要在线程中等待。如果线程正在等待不间断IO,那么您可以查看

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

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


如果您正在等待某些内容,我认为您必须捕获,不是吗?

每个行为良好的阻塞方法都会声明一个checked exception
InterruptedException
,它的确切用途是:通知线程在阻塞时已被中断

您必须捕获此异常,实际上这可以替换
stop
字段

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

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在终止之前调用close()(因此中断线程,刷新并关闭文件)。

您的构造不正确,
stop
变量必须是
volatile
@Leif Ericson:实际上,如果您的程序是多线程的,您可能正在调用stop())方法:因此,您希望将private boolean stop=false更改为private volatile boolean stop=false,因为否则,无法保证run()方法在您从另一个线程更改后读取正确的停止值。@denis您的第一条语句是正确的(OP的构造不正确)。您的第二个语句不正确,因为您还可以使用方法调用。@denis我的意思是,如果您在(!shouldStop())时使用了
,则不需要将其设置为
易失性的
。您的声明说它必须是volatile,虽然它是最简单的解决方案,但不是唯一的解决方案。只有西斯是绝对的(绝地武士对西斯的观察除外)。@glowcoder如果你在(!shouldStop())
时做了
shouldStop()
你需要将
shouldStop()和
stop()
标记为
同步的
。在这种情况下,是的,你是对的:)我别无选择。线程必须在我的程序中等待。我可以使用
wait(X)
,但我不能只是抛出一个随机数。如果你想让线程无限期地等待,请编写代码让它们这样做。但不要让他们无限期地等待,然后抱怨他们无限期地等待。仔细考虑一下您希望线程做什么,然后编写代码让它们这样做。这样,你就不必想办法“让他们”做你想做的事。
class Logger {
  ...
  {
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
      @Override public void run() { close(); }
    }));
  }
}