同步操作如果遇到耗时的方法,需要阻塞等待,如何解决阻塞等待的问题?可以将一些耗时的方法放到异步操作中。
1. Runnable 异步执行
第一步:实现Runnable接口
/**
* 实现Runnable接口
*/
public class MyRunnable implements Runnable {
/**
* 重写run(),将耗时操作放到里面
*/
@Override
public void run() {
try {
// 这里睡眠5s来模拟耗时操作
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
第二步:启动新线程异步执行
public class Test1 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = null;
try {
// 创建一个线程池,需要线程就从线程池获取,避免反复创建、销毁线程带来的开销
threadPoolExecutor = new ThreadPoolExecutor(10, 20,
1, TimeUnit.MINUTES, new ArrayBlockingQueue(10));
System.out.println("异步执行前");
// 执行
threadPoolExecutor.execute(new MyRunnable());
System.out.println("异步执行后");
} finally {
if (threadPoolExecutor != null) {
threadPoolExecutor.shutdown();
}
}
}
}
执行结果:从打印结果可以看出,并不需要等异步执行完才往下执行。
异步执行前
异步执行后
开始异步
异步结束
Process finished with exit code 0
2. Callable 异步执行
Runnable 虽然可以实现异步执行,但是 Runnable 对于异步执行不会有返回结果。如果需要异步执行的返回结果,则需要采用 Callable 实现异步执行。
第一步:实现Callable接口
/**
* 实现Callable接口,指定的泛型就是返回的结果类型
*/
public class MyCallable implements Callable<String> {
// 重写call(),将耗时操作放在里面
@Override
public String call() throws Exception {
try {
System.out.println("开始异步");
// 这里睡眠5s来模拟耗时操作
Thread.sleep(5000);
System.out.println("异步结束");
return "成功";
} catch (InterruptedException e) {
e.printStackTrace();
return "失败";
}
}
}
第二步:启动新线程异步执行
public class Test1 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = null;
try {
// 创建一个线程池,需要线程就从线程池获取,避免反复创建、销毁线程带来的开销
threadPoolExecutor = new ThreadPoolExecutor(10, 20,
1, TimeUnit.MINUTES, new ArrayBlockingQueue(10));
System.out.println("异步执行前");
// 执行
Future<String> submit = threadPoolExecutor.submit(new MyCallable());
System.out.println("异步执行后");
} finally {
if (threadPoolExecutor != null) {
threadPoolExecutor.shutdown();
}
}
}
}
执行结果:从打印结果可以看出,并不需要等异步执行完才往下执行。
异步执行前
异步执行后
开始异步
异步结束
Process finished with exit code 0
如果我们需要从Future中获取异步执行的结果,调用Future的get(),但是这个方法会阻塞等待直至异步执行完毕。
public class Test1 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = null;
try {
// 创建一个线程池,需要线程就从线程池获取,避免反复创建、销毁线程带来的开销
threadPoolExecutor = new ThreadPoolExecutor(10, 20,
1, TimeUnit.MINUTES, new ArrayBlockingQueue(10));
System.out.println("异步执行前");
// 执行
Future<String> submit = threadPoolExecutor.submit(new MyCallable());
// 获取异步执行的结果
String result = submit.get();
System.out.println("异步执行的结果:" + result);
System.out.println("异步执行后");
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
} finally {
if (threadPoolExecutor != null) {
threadPoolExecutor.shutdown();
}
}
}
}
执行结果:
异步执行前
开始异步
异步结束
异步执行的结果:成功
异步执行后
Process finished with exit code 0
3. CompletableFuture 异步执行
Runnable + Thread 可以实现异步但是没有返回值;Callable + Thread 可以实现异步并且有返回值,但是获取返回值会等待阻塞直至异步结束。
因此,JDK8 中引入了 CompletableFuture,它实现了 Future 接口和 CompletionStage。Future 让它具备接受异步执行结果的能力;CompletionStage 用于异步执行中的阶段处理,提供了一系列的方法。 
3.1 CompletionStage方法介绍
3.1.1 方法分类
一、产出型:用上一个阶段的结果作为指定函数的参数,执行函数会产生新的结果。这一类接口方法名中基本都含apply,参数是(Bi)Function类型。
二、消费型:用上一个阶段的结果作为指定操作的参数,执行指定的操作,但不对阶段结果产生影响。这一类方法名中基本都含accept,参数是(Bi)Consumer类型。
三、不产出型不消费:不依据上一个阶段的执行结果,只要上一个阶段正常完成,才执行指定的操作,且不对阶段的结果产生影响。这一类方法名中基本含run,参数是Runnable类型。
3.1.2 方法展示
public class Test1 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = null;
try {
// 创建一个线程池,需要线程就从线程池获取,避免反复创建、销毁线程带来的开销
threadPoolExecutor = new ThreadPoolExecutor(10, 20,
1, TimeUnit.MINUTES, new ArrayBlockingQueue(10));
System.out.println("异步执行前");
// supplyAsync() 开启一个异步操作
// thenAccept() 当异步操作结束,会把异步执行结果传给它,不会阻塞后面的操作
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "异步执行结果";
}, threadPoolExecutor).thenAccept(System.out::println);
System.out.println("异步执行后");
} finally {
if (threadPoolExecutor != null) {
threadPoolExecutor.shutdown();
}
}
}
}
异步执行前
异步执行后
异步执行结果
Process finished with exit code 0
CompletableFuture 提供了非常多的方法,可以满足不同的使用场景,大家可以结合自己的需求去调用不同的方法。这里只是给大家抛砖引玉,写了一个简单的示例。
194

被折叠的 条评论
为什么被折叠?



