1)想要得到Future任务的结果,使用 get()
方法阻塞线程直到结果返回。
FutureTask<String> futureTask = new FutureTask<String>(() -> {
System.out.println(Thread.currentThread().getName() + "\t -----come in");
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task over";
});
Thread t1 = new Thread(futureTask, "t1");
t1.start();
System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");
System.out.println(futureTask.get());
2)不想阻塞那就采用 isDone()轮询
,轮询的方式会耗费无谓的cpu资源,而且也不见得能够及时获得结果。
FutureTask<String> futureTask = new FutureTask<String>(() -> {
System.out.println(Thread.currentThread().getName() + "\t -----come in");
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "task over";
});
Thread t1 = new Thread(futureTask, "t1");
t1.start();
System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");
while (true){
if(futureTask.isDone()){
System.out.println(futureTask.get());
break;
}else {
//暂停毫秒
TimeUnit.MILLISECONDS.sleep(500);
System.out.println("正在处理中,不要再催了!");
}
}
这两种方法都违背了“异步”思想。所以我们需要一个更加强大的类去实现方法的“异步”执行。于是就有了 CompletableFuture
。
在java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合 CompletableFuture 的方法。
它可能代表一个明确完成的 Future,也有可能代表一个完成阶段(CompletionStage) ,它支持在计算完成以后触发一些函数或执行某些动作。
它还实现了Future和CompletionStage接口。
CompletableFuture提供了核心的四个静态方法来创建一个异步操作:
如果指定了线程池则使用自己的线程池,没有的话默认使用系统提供的。
1)runAsync无返回值,不指定线程池
//方式1,没有返回值,并且不指定线程池
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(completableFuture.get());
2)runAsync无返回值,指定线程池
//方式2,有返回值,并且指定线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
},threadPool);
System.out.println(completableFuture.get());
//关闭线程池
threadPool.shutdown();
3)supplyAsync有返回值,不指定线程池
//方式3,有返回值,不指定线程池
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello world";
});
System.out.println(completableFuture.get());
4)supplyAsync有返回值,指定线程池
//方式4,有返回值,指定线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello world";
},threadPool);
System.out.println(completableFuture.get());
//关闭线程池
threadPool.shutdown();
从Java8开始引入了CompletableFuture,它是Future的增强版,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。
CompletableFuture有很多优点,比如:
public static void main(String[] args) throws ExecutionException, InterruptedException {
//future1();
ExecutorService threadPool = Executors.newFixedThreadPool(3);
try {
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
//异步任务。。。
log.info("当前线程名:{}", Thread.currentThread().getName());
//int i=1/0;
int result = ThreadLocalRandom.current().nextInt(10);
log.info("----3s后出结果:{}", result);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}, threadPool).whenComplete((v, e) -> {
//回调方法
if (e == null) {
log.info("计算完成,更新updateValue值为:{}", v);
}
}).exceptionally(e -> {
//发生错误时的回调方法
e.printStackTrace();
log.info("异常情况:{},\\t{}", e.getCause(), e.getMessage());
return null;
});
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
log.info("{}线程先去忙别的任务了", Thread.currentThread().getName());
}
这种写法和js的请求方法回调类似。在 whenComplete
中执行任务正常完成后的操作,在 exceptionally
中执行任务发生异常后的操作。
在Java的CompletableFuture类中,get()和join()也都是用于获取Future的执行结果的方法。它们的主要区别在于返回值和异常处理上。
join()方法会等待当前CompletableFuture对象的执行结果,如果执行过程中出现了异常,则会抛出Unchecked Exception。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//do something and return result
return result;
});
String result = future.join(); //等待future执行完毕,获取结果
//如果future执行过程中出现了异常,则会抛出Unchecked Exception
get()方法也会等待当前CompletableFuture对象的执行结果,但不同的是,如果执行过程中出现了异常,则会抛出Checked Exception(ExecutionException),这需要进行捕获或者声明抛出。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
//do something and return result
return result;
});
try {
String result = future.get(); //等待future执行完毕,获取结果
} catch (InterruptedException e) {
//处理InterruptedException异常
} catch (ExecutionException e) {
//处理ExecutionException异常
}
如果你不想在代码中显式地处理异常,并且可以确定CompletableFuture执行过程中不会出现异常,那么可以使用join()方法。否则,如果你需要对执行过程中出现的异常进行处理,可以使用get()方法。
获取结果:
public T getNow(T valueIfAbsent):立刻返回结果,没有计算完成的情况下给一个替代的结果(valueIfAbsent)。
主动触发计算:
public boolean complete(T value):没有计算完成则打断任务并将结果设置为value。
thenApply:计算结果存在依赖关系,线程之间串行化,由于存在依赖关系,如果当前计算过程出错,则不会走到下一过程计算。
方法如下:
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
示例:
@Test
public void test() {
long start = System.currentTimeMillis();
//当一个线程依赖另一个线程时用 thenApply 方法来把这两个线程串行化,
CompletableFuture.supplyAsync(() -> {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("111");
return 1024;
}).thenApply(f -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("222");
return f + 1;
}).thenApply(f -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//int age = 10/0; // 异常情况:那步出错就停在那步。
System.out.println("333");
return f + 1;
}).whenCompleteAsync((v, e) -> {
System.out.println("*****v: " + v);
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start)+"ms");
}).exceptionally(e -> {
e.printStackTrace();
return null;
});
System.out.println("-----主线程结束,END");
// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果:
handle:计算结果存在依赖关系,线程之间串行化,当前计算过程有错则跳过,直接进入下一计算过程。
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) {
return uniHandleStage(null, fn);
}
示例:
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(() -> {
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("111");
return 1;
}, threadPool).handle((f, e) -> {
int i = 10 / 0;
System.out.println("222");
return f + 2;
}).handle((f, e) -> {
System.out.println("333");
return f + 3;
}).whenComplete((v, e) -> {
if (e == null) {
System.out.println("----计算结果: " + v);
}
}).exceptionally(e -> {
e.printStackTrace();
System.out.println(e.getMessage());
return null;
});
System.out.println(Thread.currentThread().getName() + "----主线程先去忙其它任务");
threadPool.shutdown();
}
运行结果:
whenComplete()和
whenCompleteAsync()的区别:
whenComplete()
方法是在当前线程中执行回调操作,即会阻塞当前线程直到回调操作执行完毕。如果异步任务已经完成,则立即执行回调操作;否则等待异步任务完成后再执行回调操作。whenComplete()
方法是在当前线程中执行回调操作,即会阻塞当前线程直到回调操作执行完毕。如果异步任务已经完成,则立即执行回调操作;否则等待异步任务完成后再执行回调操作。whenComplete()
方法会阻塞当前线程,而whenCompleteAsync()
方法不会阻塞当前线程,会在新的线程中执行回调操作。接受任务的处理结果并消费,无返回结果。
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
示例:
@Test
public void test(){
CompletableFuture.supplyAsync(() -> {
return 1;
}).thenApply(f -> {
return f + 2;
}).thenApply(f -> {
return f + 3;
}).thenApply(f -> {
return f + 4;
}).thenAccept(r -> System.out.println(r));
}
运行结果为:10
thenRun(Runnable runnable):任务A执行完再执行B,并且B不需要A的结果
thenAccept(Consumer action):任务A执行完再执行B,B需要A的结果,B无返回值
thenApply(Function fn):任务A执行完再执行B,B需要A的结果,B有返回值
示例:
public static void main(String[] args){
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(r -> System.out.println(r)).join());
System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(r -> r + "resultB").join());
}
运行结果如下:
谁快就用谁
方法如下:
public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) {
return orApplyStage(null, other, fn);
}
示例:
public static void main(String[] args)
{
CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {
System.out.println("A come in");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return "playA";
});
CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {
System.out.println("B come in");
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
return "playB";
});
CompletableFuture<String> result = playA.applyToEither(playB, f -> {
return f + " is winer";
});
System.out.println(Thread.currentThread().getName()+"\t"+"-----: "+result.join());
}
运行结果如下:
作用:返回一个新的CompletionStage,当该阶段和另一个给定阶段都正常完成时,该阶段将以两个结果作为所提供函数的参数执行。
方法如下:
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) {
return biApplyStage(null, other, fn);
}
示例:
public static void main(String[] args) {
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
log.info("{}\t ---A启动" + Thread.currentThread().getName());
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 20;
});
CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
log.info("{}\t ---B启动" + Thread.currentThread().getName());
//暂停几秒钟线程
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 10;
});
CompletableFuture<Integer> result = completableFuture1.thenCombine(completableFuture2, (x, y) -> {
log.info("-----开始两个结果合并");
return x + y;
});
log.info("{}",result.join());
}
运行结果如下: