目录
ExecutorCompletionService获取多个子线程返回值
Java调用shell并打印输出结果
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
Process proc = Runtime.getRuntime().exec("pwd");
InputStream inputStream = proc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
proc.waitFor();
}
}
多线程
多线程部分,方便查找,就不拆分到其他篇了。
实现多线程的方式包括继承Thread,实现Runnable或Callable<V>接口,前两者是没有返回体的(Callable<V>的V即为我们返回体的数据类型),本篇不对前两种方式进行讲解,仅介绍Callable。
FutureTask获取子线程返回值
FutureTask适合开启单个子线程并在主线程中监控、阻塞获取子线程的返回值,由于其get()方法有阻塞,如果需要获取多个子线程的返回结果,主线程中需要等子线程挨个执行完(因为需要挨个获取执行结果),达不到好的多线程效果,以下是示例:
- 先编写子线程类(不带任何逻辑,打印-睡3秒-返回结束语句):
import java.util.concurrent.Callable;
public class UglyRunner implements Callable<String> {
@Override
public String call() throws Exception {
String threadName = Thread.currentThread().getName();
System.out.println(String.format("%s start to run: %s", threadName, new Date()));
System.out.println(threadName + ": Please hold on, I'm running....");
Thread.sleep(3000);
System.out.println(String.format("%s stop running: %s", threadName, new Date()));
return threadName + ": I'm out now";
}
}
- 再编写主类(循环检查子线程执行是否完成,子线程执行完成后获取其返回值并打印):
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class WaitSubRes {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new UglyRunner());
Thread thread = new Thread(futureTask);
thread.start();
// 每1秒循环检查一次子线程是否完成执行
while (!futureTask.isDone()) {
System.out.println(Thread.currentThread().getName() + ": the sub task is still running....");
Thread.sleep(1000);
}
System.out.println(futureTask.get());
}
}
以上代码,主线程循环检查了子线程的运行状态,并在子线程运行完毕后打印了子线程的返回值。接下来,尝试在启动主类中创建多个子线程。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
public class WaitMultiSubRes {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(3);
UglyRunner uglyRunner = new UglyRunner();
for (int i = 0; i < 3; i++) {
FutureTask<String> futureTask = new FutureTask<>(uglyRunner);
executor.submit(futureTask);
while (!futureTask.isDone()) {
System.out.println(Thread.currentThread().getName() + ": the sub task is still running....");
Thread.sleep(1000);
}
System.out.println(futureTask.get());
}
executor.shutdown();
}
}
这里用了线程池的写法,不过无关大局。通过观察打印输出结果,可以发现三个子线程是顺序执行的(红线框为开始时间,绿线框为结束时间):
实际开发中,我们肯定不喜欢这样的结果,阻塞影响了多线程只能逐个进行,需要换一种方法,下面介绍。
ExecutorCompletionService获取多个子线程返回值
ExecutorCompletionService提供的take()方法可以阻塞获取子线程返回值,批量任务的返回值只需调用同样次数的take()即可,先完成运行的子线程返回值先被获取到。以下为相关代码:
import java.util.concurrent.*;
public class WaitMultiSubRes2 {
public static int nThreads = 3;
public static void main(String[] args) throws InterruptedException, ExecutionException {
UglyRunner uglyRunner = new UglyRunner();
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletionService<String> completionService = new ExecutorCompletionService<>(executorService);
for (int i = 0; i < nThreads; i++) {
completionService.submit(uglyRunner);
}
String result = "";
for (int i = 0; i < nThreads; i++) {
result = String.format("%s %s", result, completionService.take().get());
}
System.out.println(result);
executorService.shutdown();
}
}
运行结果:
可以看到,3个子线程在同一时间并行启动,而不是FutureTask那样串行启动。