多线程编程中,对于干活得线程不会将任务的返回值带出来,而且对于exception 也只能catch,没法throw。
Java 提供了Callable 接口
public interface Callable<V> {
V call() throws Exception;
}
实现这个接口的类就是可以被其他线程执行的任务,返回值和异常抛出就很明了了。而且可以和Future 类配合,来了解任务执行情况,或者取消任务,也能获取返回结果。在Flink中算子很多就这么玩的。
除了向线程池submit一个callable任务,也可以用FutureTask来获取callable结果
先看下FutureTask实现
public class FutureTask<V> implements RunnableFuture<V>{
...
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
如此,可以用Callable对象构造一个FutureTask,再新启一个线程来执行这个FutureTask。如下。
public class FutureTaskDemo {
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
new Thread(integerFutureTask).start();
try {
System.out.println("task运行结果:"+integerFutureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程正在计算");
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
对于一个并发请求的场景,比如同时请求三个地址,都是3s超时,这样的场景下每个地址的响应时间是一定的。可能每个地址都在毫秒级相应,也可能有一个地址会超时,但是另外两个地址都很快相应,这就需要一种灵活的机制。如果使用线程池来实现的话可以结合countDownlatch来做到既可以提前返回也可以超时抛异常。类似golang也可以用这种方式。
但是java 还提供了 CompletableFuture 的方法,例如
public class CompletableFutureDemo {
public static void main(String[] args)
throws Exception {
CompletableFutureDemo completableFutureDemo = new CompletableFutureDemo();
System.out.println(completableFutureDemo.getPrices());
}
private Set<Integer> getPrices() {
Set<Integer> prices = Collections.synchronizedSet(new HashSet<Integer>());
CompletableFuture<Void> task1 = CompletableFuture.runAsync(new Task(123, prices));
CompletableFuture<Void> task2 = CompletableFuture.runAsync(new Task(456, prices));
CompletableFuture<Void> task3 = CompletableFuture.runAsync(new Task(789, prices));
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2, task3);
try {
allTasks.get(3, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} catch (ExecutionException e) {
} catch (TimeoutException e) {
}
return prices;
}
private class Task implements Runnable {
Integer productId;
Set<Integer> prices;
public Task(Integer productId, Set<Integer> prices) {
this.productId = productId;
this.prices = prices;
}
@Override
public void run() {
int price = 0;
try {
Thread.sleep((long) (Math.random() * 4000));
price = (int) (Math.random() * 4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
prices.add(price);
}
}
}