java中的子流程非常昂贵。每个进程通常由数字线程支持。
如果你有一个线程focking子进程池来完成任务,那么线程的数量就会失控。因此,峰值时可能会有超过两倍的并发线程。
在许多情况下,我们分叉一个进程只是因为没有人能够编写JNI来调用JDK中缺少的本机函数(例如chmod,ln,ls),触发shell脚本等等。
可以保存一些线程,但是应该运行一些线程以防止最坏的情况(输入流上的缓冲区溢出)。
如何将Java中创建子流程的开销降至最低? 我在考虑NIO流处理,组合和共享线程,降低后台线程优先级,重用进程。但我不知道它们是否可能。
答案 0 :(得分:3)
JDK7将解决此问题并在ProcessBuilder中提供新的API redirectOutput / redirectError以重定向stdout / stderr。
然而坏消息是,他们忘记提供“Redirect.toNull”,这意味着你想要做的事情就像“if(* nix)/ dev / null elsif(win)nil”
不可信的是,NIO / 2 api for Process仍然缺失;但我认为redirectOutput + NIO2的AsynchronizeChannel会有所帮助。
答案 1 :(得分:2)
我创建了一个开源库,允许java和子进程之间的非阻塞I / O.该库提供了一个事件驱动的回调模型。这取决于JNA库使用特定于平台的本机API,例如Linux上的epoll,MacOS X上的kqueue / kevent或Windows上的IO完成端口。
该项目名为NuProcess,可在此处找到:
答案 2 :(得分:1)
要回答你的主题(我不理解描述),我假设你的意思是shell子进程输出,检查这些SO问题:
platform-independent /dev/null output sink for Java
Is there a Null OutputStream in Java?
或者你可以关闭stdout和stderr来执行在Unix下执行的命令:
command > /dev/null 2>&1
答案 3 :(得分:1)
nio不起作用,因为在创建流程时,您只能访问OutputStream,而不能访问Channel。
您可以让1个线程读取多个InputStream。
类似的东西,
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
class MultiSwallower implements Runnable {
private List<InputStream> streams = new CopyOnWriteArrayList<InputStream>();
public void addStream(InputStream s) {
streams.add(s);
}
public void removeStream(InputStream s) {
streams.remove(s);
}
public void run() {
byte[] buffer = new byte[1024];
while(true) {
boolean sleep = true;
for(InputStream s : streams) {
//available tells you how many bytes you can read without blocking
while(s.available() > 0) {
//do what you want with the output here
s.read(buffer, 0, Math.min(s.available(), 1024));
sleep = false;
}
}
if(sleep) {
//if nothing is available now
//sleep
Thread.sleep(50);
}
}
}
}
您可以将上述类与另一个等待进程完成的类配对,例如
class ProcessWatcher implements Runnable {
private MultiSwallower swallower = new MultiSwallower();
private ConcurrentMap<Process, InputStream> proceses = new ConcurrentHashMap<Process, InputStream>();
public ProcessWatcher() {
}
public void startThreads() {
new Thread(this).start();
new Thread(swallower).start();
}
public void addProcess(Process p) {
swallower.add(p.getInputStream());
proceses.put(p, p.getInputStream());
}
@Override
public void run() {
while(true) {
for(Process p : proceses.keySet()) {
try {
//will throw if the process has not completed
p.exitValue();
InputStream s = proceses.remove(p);
swallower.removeStream(s);
} catch(IllegalThreadStateException e) {
//process not completed, ignore
}
}
//wait before checking again
Thread.sleep(50);
}
}
}
同样,如果使用ProcessBuilder.redirectErrorStream(true),则不需要为每个错误流设置1个线程,并且您不需要1个线程来读取进程输入流,您可以简单地忽略输入如果你没有写任何东西流。
答案 4 :(得分:1)
你不需要任何额外的线程来在java中运行子进程,虽然处理超时会使事情变得复杂:
import java.io.IOException;
import java.io.InputStream;
public class ProcessTest {
public static void main(String[] args) throws IOException {
long timeout = 10;
ProcessBuilder builder = new ProcessBuilder("cmd", "a.cmd");
builder.redirectErrorStream(true); // so we can ignore the error stream
Process process = builder.start();
InputStream out = process.getInputStream();
long endTime = System.currentTimeMillis() + timeout;
while (isAlive(process) && System.currentTimeMillis() < endTime) {
int n = out.available();
if (n > 0) {
// out.skip(n);
byte[] b = new byte[n];
out.read(b, 0, n);
System.out.println(new String(b, 0, n));
}
try {
Thread.sleep(10);
}
catch (InterruptedException e) {
}
}
if (isAlive(process)) {
process.destroy();
System.out.println("timeout");
}
else {
System.out.println(process.exitValue());
}
}
public static boolean isAlive(Process p) {
try {
p.exitValue();
return false;
}
catch (IllegalThreadStateException e) {
return true;
}
}
}
您还可以使用[{3}}中的反射来从FileChannel
获取NIO Process.getInputStream()
,但是您必须担心不同的JDK版本以换取摆脱民意调查。
答案 5 :(得分:0)
由于您提及chmod
,ln
,ls
和shell脚本,听起来您正在尝试使用Java进行shell编程。如果是这样,您可能需要考虑更适合该任务的其他语言,例如Python,Perl或Bash。尽管在Java中创建子进程当然是可能的,但是通过它们的标准输入/输出/错误流等与它们进行交互,我认为你会发现一种脚本语言使得这种代码比Java更简洁,更容易维护。
答案 6 :(得分:0)
您是否可以使用JNA编写本机调用?
请参阅How do i programmatically change file permissions?的答案,了解chmod的一个很好的例子。
比JNI容易得多,并且比子流程快得多!
答案 7 :(得分:0)
您是否考虑过使用另一种语言(可能是shell脚本?)编写的长时间运行的辅助进程,该进程将通过stdin使用来自java的命令并执行文件操作以响应?