我有一个工作线程池(ExecutorService
)。
此池用于运行shell命令。
我使用shell(/bin/sh
)而不是直接为可执行文件创建进程,因为我使用shell重定向(>
)将输出直接写入磁盘,而不必通过JVM,以及其他一些细节。
生成shell进程需要2-3毫秒。
我希望每个线程都保留一个shell进程,以避免启动它的开销。
如何让每个线程拥有一个进程?
我正在考虑使用带有线程本地的ThreadFactory
。
class ThreadFactory {
Thread newThread(Runnable r) {
return new Thread(new Runnable() {
Process process = Runtime.getRuntime().exec("/bin/sh")
try {
// store process as thread local here
r.run(); // then r can access thread local
} catch(Exception e) {
try {
process.close();
} catch(Exception e) {
}
throw e;
}
});
}
}
(或者,我可以将Thread
子类化并将Thread.currentThread()
投射到我Runnable
中的该类。)
这是解决这个问题的好方法吗?
答案 0 :(得分:1)
我会将Process
引用保存在连续执行命令的ProcessRunnable
中。我认为比使用ThreadLocal
和ThreadFactory
更清楚。像这样:
public class ShellCommandExecutor {
private int concurrency = 10;
private int capacity = 100;
private ExecutorService service = Executors.newFixedThreadPool(concurrency);
private BlockingQueue<String> commandsQueue = new LinkedBlockingQueue<>(capacity);
public void start() {
for (int i = 0; i < concurrency; i++)
service.submit(new Runnable() {
@Override
public void run() {
//todo deal with ioexception
Process process = Runtime.getRuntime().exec("/bin/sh");
while (!Thread.currentThread().isInterrupted()) {
try {
String command = commandsQueue.take();
//todo execute commands using the same process per thread
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
});
}
public void executeCommand(String command) throws InterruptedException {
commandsQueue.put(command);
}
public void shutdown() {
service.shutdownNow();
}
}
编辑:具有本地线程的解决方案,可以轻松使用缓存的线程池:
public class ShellCommandExecutor2 {
//todo limit queue
private ExecutorService service = Executors.newCachedThreadPool();
public void executeCommand(final String command) throws InterruptedException {
service.submit(new Runnable() {
@Override
public void run() {
Process process = ThreadLocalProcessFactory.get();
//todo execute command
}
});
}
public void shutdown() {
service.shutdownNow();
}
private static class ThreadLocalProcessFactory {
private static final ThreadLocal<Process> processThreadLocal =
new ThreadLocal<Process>() {
@Override protected Process initialValue() {
try {
return Runtime.getRuntime().exec("/bin/sh");
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}
};
static Process get() {
return processThreadLocal.get();
}
}
}