如何从java.lang.Process关闭std流?
这个问题是关于如何从java.lang.Process关闭std流?,java,process,stream,resources,Java,Process,Stream,Resources,这个问题是关于java.lang.Process及其对stdin、stdout和stderr的处理 我们的项目中有一个类,它是org.apache.commons.io.IOUtils的扩展。我们有一个安静的新方法来关闭进程对象的std流?还是不合适 /** * Method closes all underlying streams from the given Process object. * If Exit-Code is not equal to 0 then Process wi
java.lang.Process
及其对stdin、stdout和stderr的处理
我们的项目中有一个类,它是org.apache.commons.io.IOUtils的扩展。我们有一个安静的新方法来关闭进程对象的std流?还是不合适
/**
* Method closes all underlying streams from the given Process object.
* If Exit-Code is not equal to 0 then Process will be destroyed after
* closing the streams.
*
* It is guaranteed that everything possible is done to release resources
* even when Throwables are thrown in between.
*
* In case of occurances of multiple Throwables then the first occured
* Throwable will be thrown as Error, RuntimeException or (masked) IOException.
*
* The method is null-safe.
*/
public static void close(@Nullable Process process) throws IOException {
if(process == null) {
return;
}
Throwable t = null;
try {
close(process.getOutputStream());
}
catch(Throwable e) {
t = e;
}
try{
close(process.getInputStream());
}
catch(Throwable e) {
t = (t == null) ? e : t;
}
try{
close(process.getErrorStream());
}
catch (Throwable e) {
t = (t == null) ? e : t;
}
try{
try {
if(process.waitFor() != 0){
process.destroy();
}
}
catch(InterruptedException e) {
t = (t == null) ? e : t;
process.destroy();
}
}
catch (Throwable e) {
t = (t == null) ? e : t;
}
if(t != null) {
if(t instanceof Error) {
throw (Error) t;
}
if(t instanceof RuntimeException) {
throw (RuntimeException) t;
}
throw t instanceof IOException ? (IOException) t : new IOException(t);
}
}
类似的方法基本上用于finally块 我真正想知道的是,我使用这个实现是否安全?考虑以下情况:进程对象在其生命周期内是否总是返回相同的stdin、stdout和stderr流?或者我可能会错过关闭以前由进程“
getInputStream()
,getOutputStream()
和getErrorStream()
方法返回的流吗
StackOverflow.com上有一个相关问题:
编辑
正如我和其他人在这里指出的那样:
- 必须完全消耗输入流。如果未完成,则子流程可能不会终止,因为其输出流中存在未完成的数据
- 所有三个std流都必须关闭。无论之前是否使用过
- 当子流程正常终止时,一切正常。如果没有,则必须强制终止
- 当子流程返回退出代码时,我们不需要销毁它。它已经终止。(即使不一定以退出代码0正常终止,但它已终止。)
- 我们需要监视
并在超时超过时中断,以使进程有机会正常终止,但在挂起时终止它waitFor()
- 考虑并行使用输入流的利弊。还是必须按特定顺序食用
- 简化代码的尝试:
public static void close(@Nullable Process process) throws IOException
{
if(process == null) { return; }
try
{
close(process.getOutputStream());
close(process.getInputStream());
close(process.getErrorStream());
if(process.waitFor() != 0)
{
process.destroy();
}
}
catch(InterruptedException e)
{
process.destroy();
}
catch (RuntimeException e)
{
throw (e instanceof IOException) ? e : new IOException(e);
}
}
通过捕获可丢弃的Throwable
我假设您希望捕获所有未检查的异常。这是运行时异常
或错误
的派生。但是错误
永远不应该被捕获,所以我用运行时异常
替换了可丢弃的
(捕获所有的
运行时异常仍然不是一个好主意。)作为链接到状态的问题,最好读取并丢弃输出和错误流。如果您使用的是apache commons io
new Thread(new Runnable() {public void run() {IOUtils.copy(process.getInputStream(), new NullOutputStream());}}).start();
new Thread(new Runnable() {public void run() {IOUtils.copy(process.getErrorStream(), new NullOutputStream());}}).start();
您希望在一个单独的线程中读取和丢弃stdout和stderr,以避免在将足够的信息写入stderr或stdout以填充缓冲区时出现诸如进程阻塞之类的问题
如果您担心有两个多线程,请参见此
我认为在将stdout、stdin复制到NullOutputStream时,您不必担心捕捉IOException,因为如果从进程stdout/stdin读取IOException,可能是由于进程本身已死亡,写入NullOutputStream将永远不会引发异常
您不需要检查waitFor()的返回状态
是否要等待进程完成?如果是的话,你可以这样做
while(true) {
try
{
process.waitFor();
break;
} catch(InterruptedException e) {
//ignore, spurious interrupted exceptions can occur
}
}
查看您提供的链接,您确实需要在流程完成时关闭流,但destroy将为您完成此操作
所以最后,这个方法变成
public void close(Process process) {
if(process == null) return;
new Thread(new Runnable() {public void run() {IOUtils.copy(process.getInputStream(), new NullOutputStream());}}).start();
new Thread(new Runnable() {public void run() {IOUtils.copy(process.getErrorStream(), new NullOutputStream());}}).start();
while(true) {
try
{
process.waitFor();
//this will close stdin, stdout and stderr for the process
process.destroy();
break;
} catch(InterruptedException e) {
//ignore, spurious interrupted exceptions can occur
}
}
}
只是想让您知道我目前在我们的代码库中拥有什么:
public static void close(@Nullable Process process) throws IOException {
if (process == null) {
return;
}
Throwable t = null;
try {
flushQuietly(process.getOutputStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
close(process.getOutputStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
skipAllQuietly(null, TIMEOUT, process.getInputStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
close(process.getInputStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
skipAllQuietly(null, TIMEOUT, process.getErrorStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
close(process.getErrorStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
try {
Thread monitor = ThreadMonitor.start(TIMEOUT);
process.waitFor();
ThreadMonitor.stop(monitor);
}
catch (InterruptedException e) {
t = mostImportantThrowable(t, e);
process.destroy();
}
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
if (t != null) {
if (t instanceof Error) {
throw (Error) t;
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
throw t instanceof IOException ? (IOException) t : new IOException(t);
}
}
skipall(…)
使用完整的输入流。它在内部使用一个类似于org.apache.commons.io.ThreadMonitor
的实现,在超过给定超时时中断使用
mostImportantThrowable(…)
决定应返回哪些Throwable。一切都是错误。第一次发生的prio高于第二次发生的prio。这里没有什么很重要的,因为这些一次性的东西很可能会在以后被丢弃。我们想继续在这里工作,我们只能扔一个,所以我们必须决定最后扔什么,如果有的话
close(…)
是空安全的实现,用于关闭内容,但在出现问题时引发异常。请注意。您的代码中有一些严重的反模式。1.几乎每一条语句都要尝试/捕捉。2.每次都捕捉到所有“可丢弃”的东西。3.嵌套的try语句也可以是一个,有两个catch语句。如果不将其他流放入单独的try-catch块中,如何继续关闭其他流?必须使用嵌套的try-catch块,因为嵌套的catch块中可能会出现Throwable。对于可丢弃的捕获:什么更合适?我想抓住一切值得继续努力的东西,尽我最大的努力。当我以前被抓住时,我会在最后扔一个一次性的。在这里我能做得更好的是什么?请注意,这里是一个实用程序类,用于关闭其他地方的finally块中的资源。处理异常不是它的职责。它抛出第一个发生的异常。打电话的人必须处理这件事。但它的目的是尽一切可能释放调用者提供的资源。当关闭outputstream之前发生异常时,您的代码不会尝试关闭inputstream。捕获可丢弃性的目的是提供尽可能高的保证,以便在关闭资源的情况下继续工作。如果发生了丢弃,则在最后抛出第一个出现的丢弃。之所以这样做,是因为如果出现问题,它很可能是你想要的一次性物品。在这种情况下,为什么不抓住一次性的呢?我应该捕捉异常吗?为什么?IOException
不能是RuntimeException
。所以最后一个catch块的代码可以简化。但您的实现并没有尽全力关闭流。当catch interruptedException块中发生此异常时,它可能会抛出除IOException
之外的其他异常+无论如何,因为至少有人考虑过。非常感谢你的回答。快速阅读
public void close(Process process) {
if(process == null) return;
new Thread(new Runnable() {public void run() {IOUtils.copy(process.getInputStream(), new NullOutputStream());}}).start();
new Thread(new Runnable() {public void run() {IOUtils.copy(process.getErrorStream(), new NullOutputStream());}}).start();
while(true) {
try
{
process.waitFor();
//this will close stdin, stdout and stderr for the process
process.destroy();
break;
} catch(InterruptedException e) {
//ignore, spurious interrupted exceptions can occur
}
}
}
public static void close(@Nullable Process process) throws IOException {
if (process == null) {
return;
}
Throwable t = null;
try {
flushQuietly(process.getOutputStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
close(process.getOutputStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
skipAllQuietly(null, TIMEOUT, process.getInputStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
close(process.getInputStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
skipAllQuietly(null, TIMEOUT, process.getErrorStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
close(process.getErrorStream());
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
try {
try {
Thread monitor = ThreadMonitor.start(TIMEOUT);
process.waitFor();
ThreadMonitor.stop(monitor);
}
catch (InterruptedException e) {
t = mostImportantThrowable(t, e);
process.destroy();
}
}
catch (Throwable e) {
t = mostImportantThrowable(t, e);
}
if (t != null) {
if (t instanceof Error) {
throw (Error) t;
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
throw t instanceof IOException ? (IOException) t : new IOException(t);
}
}