我试图通过关闭流来中止已经阻塞的InputStream.read(),但read()不会返回。
public static void main(String args[]) throws Exception
{
ProcessBuilder builder = new ProcessBuilder("sleep", "100000");
Process process = builder.start();
final InputStream is = process.getInputStream();
Thread worker = new Thread() {
public void run() {
try {
int data;
while ((data = is.read()) != -1) {
System.out.println("Read:" + data);
}
} catch(IOException e) {
e.printStackTrace();
}
}
};
worker.start();
Thread.sleep(2000);
// process.destroy();
is.close();
worker.join();
}
有没有办法在不调用process.destroy()的情况下通过关闭流来中止已经阻塞的read()?
这个问题的背景是一个复杂的eclipse插件,大多数时候会通过调用process.destroy()中止阻塞BufferedReader.readLine(),但有时readLine()不会中止。我怀疑与JVM内部ProcessReaper线程竞争,我正试图通过明确关闭流来解决这个问题。
我正在使用Linux和JDK 1.7.0_25-b15。
答案 0 :(得分:2)
老问题,新答案。 NuProcess库为进程实现非阻塞I / O,因此您可以完全避免这种情况。
免责声明:我是NuProcess的作者。
答案 1 :(得分:1)
您尝试了close()
但它不起作用。我能想到的另一件事就是在被阻止的线程上调用Thread.interrupt()
...但我怀疑这是否会起作用。
真正的问题是规格(即各自的javadocs)没有说明这些事情是否有效。即使它们确实有效......对于某些操作系统平台上的某些Java版本......也无法保证它们可以在其他版本/平台组合上运行。
答案 2 :(得分:1)
我见过类似的东西,在我看来,每当java线程阻塞时,都是因为子进程已经将文件描述符赋给了自己的子进程,该进程在{{1 }} 被称为。所以,示意性地看起来像这样:
Process.destroy()
我确实去了Windows和Linux平台检查java thread --> child process --> grandchild process
的源代码,结果发现Process.destroy()
命令。如果要杀死孙子,则需要kill 9
命令。我不知道在不使用JNI的情况下解决这个问题的任何好方法,但是既然你为Eclipse编写,你可以试试CDT Spawner类而不是kill -9
。我记得它提供了一些超出运行时的功能,但不记得是否可以解决这个问题。例如,此类用于通过Eclipse CDT环境启动和调试本机进程。
Bug JDK-4770092提供了解释为什么很难在Windows上杀死孙子进程的原因。据我所知,Sun / Oracle决定提供最低的共同点,这就是为什么没有孙子在Unix上被杀的原因。
再想一想,如果我错了并且Process.destroy()发送了一个SIGTERM,那么下面的shell脚本应该将所有子进程与java隔离开来,并且可以在Linux上和在Windows上的Cygwin中工作。
java.lang.Runtime
答案 3 :(得分:1)
我有一个类似的问题,可以通过从流中读取之前检查一些可用字节来解决。 修改示例:
public static void main(String args[]) throws Exception
{
ProcessBuilder builder = new ProcessBuilder("sleep", "100000");
Process process = builder.start();
final InputStream is = process.getInputStream();
final AtomicBoolean stopReading = new AtomicBoolean(false);
final long CHECK_INTERVAL = 100; // msec.
Thread worker = new Thread() {
public void run() {
try {
workerLoop:
while (true) {
int availableBytes = is.available(); // this call should be non-blocking
if (availableBytes > 0) {
byte[] buffer = new byte[availableBytes];
int c = is.read(buffer); // this call should be non-blocking
for (int i = 0; i < c; i++) {
if (buffer[i] == -1) // standard check for the end of stream
{
break workerLoop;
}
// do something with the data....
}
} else if (stopReading.get()) {
break;
} else {
Thread.sleep(CHECK_INTERVAL);
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
};
worker.start();
Thread.sleep(2000);
stopReading.set(true); // request to terminate the thread
worker.join();
is.close();
}
可以修改代码以中断工作线程,而无需使用布尔标志。
我正在使用Windows 10和Oracle Java 8。
答案 4 :(得分:-1)
我想我不能在Java 1.7中肯定地说,但我在Java 1.6中已经做到了这一点。你需要3个主题:
读取线程应更新它与监视器线程之间共享的引用,但更重要的是,监视器需要引用读取线程。读取线程更新每个读取线程的时间值读。监视器休眠一段时间,每次唤醒时都会检查该时间戳。如果前一时间和当前时间之间的差异超过某个值,则监视器会强制读取线程中断,然后自行停止。
这可以想象只用两个线程来完成 - 主线程和监视器,其中主线程将执行读取并执行monitor.setReaderThread(Thread.currentThread()); monitorThread.start()
。但我只用三个线程完成了这个。