当用户按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();
}
然而,对于看起来应该相对简单的事情,上述内容似乎不必要。有没有人知道以线程安全的方式实现这个的更好方法?
答案 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将终止所有线程