Java如何等待线程退出或用户按下Enter

时间:2017-07-11 16:18:28

标签: java multithreading io

当用户按Enter键或其他线程完成其工作时,我需要退出应用程序。我过去完成这项工作的方式如下:

// Main
final Worker worker = new Worker();
final Thread workerThread = new Thread(worker::run);
workerThread.start();
try {
    System.in.read();
} catch (final IOException e) {
    // The worker thread finished executing
}

// Worker::run
public void run() {
    ...
    System.in.close();
}

之前似乎工作得很好,但最近在执行一些重构之后就破了。现在,在另一个线程中对System.in.close()的调用中,该线程在本机方法close0中无限期地挂起在FileInputStream :: close中。谁能解释为什么这个方法调用无限期挂起,好像存在死锁情况?

我确实有一个解决方法,具体如下:

// Main.java
private static final Object waitObject = new Object();

private static boolean isRunning = true;

private static void notifyWaitObject() {
    synchronized (waitObject) {
        isRunning = false;
        waitObject.notify();
    }
}

public static void main(final String[] args) {
    // Create a thread to wait until enter is pressed
    final Thread waitThread = new Thread(() -> {
        try {
            System.in.read();
        } catch (final IOException e) {
            // Eat the exception
        } finally {
            notifyWaitObject();
        }
    }, "User Input Thread");
    waitThread.setDaemon(true);
    waitThread.start();

    // Create a worker thread
    final Worker worker = new Worker();
    final Thread workerThread = new Thread(() -> worker.run(Main::notifyWaitObject));
    workerThread.start();

    // Wait until either the worker threads are done, or enter is pressed
    synchronized (waitObject) {
        if (isRunning) waitObject.wait();
    }
}

// Worker.java
public void run(final Runnable exitNotifier) {
    ...
    exitNotifier.run();
}

然而,对于看起来应该相对简单的事情,上述内容似乎不必要。有没有人知道以线程安全的方式实现这个的更好方法?

1 个答案:

答案 0 :(得分:1)

我不知道有什么可靠的方法可以阻止线程执行旧java.io流的I / O操作,而这些流从未起作用。但是既然你说过,你想要退出应用程序,无论是工作者还是I / O,你都可以使用:

ExecutorService es = Executors.newFixedThreadPool(2);

es.invokeAny(Arrays.asList(
    () -> { worker.run(); return null; },
    () -> System.in.read() ));

es.shutdown();
System.exit(0);

invokeAny将在任一作业完成后返回,甚至在中断时取消另一项作业,对于这种情况,它支持它。

请注意,由于停止System.in.read()操作实际上不起作用,如果没有提供控制台输入,则会有一个悬空的工作线程仍在执行System.in.read(),但退出JVM将终止所有线程