1.简介
在Java中,我们经常需要处理异步的任务,比如调用远程服务,执行耗时的计算,或者等待用户的输入。传统的方法是使用Future
接口,它表示一个未完成的操作,可以通过get()
方法获取结果,或者通过isDone()
方法检查是否完成。但是,Future
接口有一些缺点,比如:
get()
方法是阻塞的,会导致当前线程等待,浪费资源和时间。Future
接口没有提供任何方法来处理结果或异常,我们只能手动编写回调逻辑,代码复杂和冗余。Future
接口不支持组合多个异步任务,我们只能嵌套多层Future
,代码可读性和维护性差。
为了解决这些问题,Java 8引入了一个新的类,叫做CompletableFuture
,它实现了Future
接口,但是提供了更多的功能和灵活性,比如:
CompletableFuture
可以通过静态方法或者构造方法创建,可以指定异步任务的执行器,也可以使用默认的ForkJoinPool.commonPool()
。CompletableFuture
可以通过一系列的方法来处理结果或异常,比如thenApply
,thenAccept
,thenCompose
,thenCombine
等,这些方法返回一个新的CompletableFuture
,形成了一个流式的编程风格。CompletableFuture
可以通过allOf
,anyOf
等方法来组合多个异步任务,可以实现并行或者并发的效果,提高程序的性能和效率。
本文将介绍CompletableFuture
的多种创建方式,结果处理方法,异常处理方法,以及超时问题,希望能够帮助你更好地使用这个强大的类,让你的程序性能起飞。
2.CompletableFuture多种创建方式
CompletableFuture
有多种创建方式,我们可以根据不同的场景选择合适的方法。
2.1 CompletableFuture.supplyAsync()
CompletableFuture.supplyAsync()
是一个静态方法,它接受一个Supplier
类型的参数,表示一个无参有返回值的函数,它会异步地执行这个函数,并返回一个CompletableFuture
对象,包含了函数的返回值。我们可以通过thenApply
等方法来处理这个返回值,也可以通过get
等方法来获取这个返回值。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 处理返回值,打印出来
future.thenAccept(System.out::println);
// 获取返回值,打印出来
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
2.2 CompletableFuture.runAsync()
CompletableFuture.runAsync()
也是一个静态方法,它接受一个Runnable
类型的参数,表示一个无参无返回值的函数,它会异步地执行这个函数,并返回一个CompletableFuture
对象,表示这个函数的执行状态。我们可以通过isDone
等方法来检查这个状态,也可以通过join
等方法来等待这个函数的完成。例如:
// 创建一个异步任务,打印一句话
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印一句话
System.out.println("Hello, CompletableFuture!");
});
// 检查状态,打印出来
System.out.println(future.isDone());
// 等待完成,打印出来
future.join();
System.out.println(future.isDone());
2.3 CompletableFuture.completedFuture()
CompletableFuture.completedFuture()
也是一个静态方法,它接受一个任意类型的参数,表示一个已经完成的值,它会返回一个CompletableFuture
对象,包含了这个值。我们可以通过thenApply
等方法来处理这个值,也可以通过get
等方法来获取这个值。这个方法主要用于测试或者和其他CompletableFuture
对象组合。例如:
// 创建一个已经完成的值,为42
CompletableFuture<Integer> future = CompletableFuture.completedFuture(42);
// 处理这个值,打印出来
future.thenAccept(System.out::println);
// 获取这个值,打印出来
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
2.4 CompletableFuture的构造方法
CompletableFuture
也可以通过构造方法来创建,它不接受任何参数,表示一个空的CompletableFuture
对象,我们可以通过complete
,completeExceptionally
等方法来手动地设置这个对象的结果或异常。这个方法主要用于自定义异步任务的执行逻辑。例如:
// 创建一个空的CompletableFuture对象
CompletableFuture<String> future = new CompletableFuture<>();
// 在另一个线程中执行异步任务
new Thread(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 根据条件设置结果或异常
if (Math.random() > 0.5) {
future.complete("Success");
} else {
future.completeExceptionally(new RuntimeException("Failure"));
}
}).start();
// 处理结果或异常,打印出来
future.whenComplete((result, exception) -> {
if (exception == null) {
System.out.println(result);
} else {
System.out.println(exception.getMessage());
}
});
3.CompletableFuture的结果
CompletableFuture
提供了一系列的方法来处理结果或异常,这些方法都返回一个新的CompletableFuture
对象,形成了一个流式的编程风格。我们可以根据不同的场景选择合适的方法。
3.1 get()方法
get()
方法是从Future
接口继承的方法,它用于获取CompletableFuture
对象的结果,如果结果还没有完成,它会阻塞当前线程,直到结果完成。它会抛出InterruptedException
或者ExecutionException
异常,如果异步任务被中断或者出现异常。这个方法比较简单,但是不推荐使用,因为它会导致线程等待,浪费资源和时间。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 获取结果,打印出来
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
3.2 thenApply()方法
thenApply()
方法是一个实例方法,它接受一个Function
类型的参数,表示一个有参有返回值的函数,它会在CompletableFuture
对象的结果完成后,异步地执行这个函数,并返回一个新的CompletableFuture
对象,包含了函数的返回值。我们可以通过thenApply
等方法来处理这个返回值,也可以通过get
等方法来获取这个返回值。这个方法主要用于对结果进行转换或者处理,比如加工,过滤,验证等。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 对结果进行转换,乘以2
CompletableFuture<Integer> doubledFuture = future.thenApply(x -> x * 2);
// 获取转换后的结果,打印出来
try {
System.out.println(doubledFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
3.3 thenAccept()方法
thenAccept()
方法是一个实例方法,它接受一个Consumer
类型的参数,表示一个有参无返回值的函数,它会在CompletableFuture
对象的结果完成后,异步地执行这个函数,并返回一个新的CompletableFuture
对象,表示这个函数的执行状态。我们可以通过isDone
等方法来检查这个状态,也可以通过join
等方法来等待这个函数的完成。这个方法主要用于对结果进行消费或者输出,比如打印,存储,发送等。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 对结果进行消费,打印出来
CompletableFuture<Void> printFuture = future.thenAccept(System.out::println);
// 检查状态,打印出来
System.out.println(printFuture.isDone());
// 等待完成,打印出来
printFuture.join();
System.out.println(printFuture.isDone());
3.4 thenCompose()方法
thenCompose()
方法是一个实例方法,它接受一个Function
类型的参数,表示一个有参有返回值的函数,它会在CompletableFuture
对象的结果完成后,异步地执行这个函数,并返回一个新的CompletableFuture
对象,包含了函数的返回值。这个函数的返回值必须是一个CompletableFuture
对象,表示一个依赖于前一个结果的异步任务。这个方法主要用于将两个异步任务串联起来,形成一个扁平的结构,而不是嵌套的结构。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 创建一个依赖于前一个结果的异步任务,返回一个字符串
CompletableFuture<String> future2 = future1.thenCompose(x -> CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个字符串
return "The number is " + x;
}));
// 获取结果,打印出来
try {
System.out.println(future2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
3.5 thenCombine()方法
thenCombine()
方法是一个实例方法,它接受两个参数,一个是另一个CompletableFuture
对象,表示一个和当前对象并行的异步任务,另一个是一个BiFunction
类型的参数,表示一个有两个参数有返回值的函数,它会在两个CompletableFuture
对象的结果都完成后,异步地执行这个函数,并返回一个新的CompletableFuture
对象,包含了函数的返回值。这个方法主要用于将两个异步任务合并起来,形成一个新的异步任务。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 创建另一个异步任务,返回一个随机数
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 将两个异步任务合并,求和
CompletableFuture<Integer> sumFuture = future1.thenCombine(future2, Integer::sum);
// 获取结果,打印出来
try {
System.out.println(sumFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
4.处理CompletableFuture的异常
CompletableFuture
提供了一些方法来处理异常,这些方法都返回一个新的CompletableFuture
对象,包含了异常处理后的结果或异常。我们可以根据不同的场景选择合适的方法。
4.1 exceptionally()方法
exceptionally()
方法是一个实例方法,它接受一个Function
类型的参数,表示一个有参有返回值的函数,它会在CompletableFuture
对象出现异常时,异步地执行这个函数,并返回一个新的CompletableFuture
对象,包含了函数的返回值。这个函数的参数是一个Throwable
对象,表示异常的原因,它的返回值是一个任意类型的对象,表示异常处理后的结果。这个方法主要用于提供一个默认的结果,或者记录一个日志,或者抛出一个新的异常。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 抛出一个异常
throw new RuntimeException("Something went wrong");
});
// 处理异常,提供一个默认的结果
CompletableFuture<Integer> defaultFuture = future.exceptionally(e -> {
// 打印异常信息
System.out.println(e.getMessage());
// 返回一个默认值
return 0;
});
// 获取结果,打印出来
try {
System.out.println(defaultFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
4.2 handle()方法
handle()
方法是一个实例方法,它接受一个BiFunction
类型的参数,表示一个有两个参数有返回值的函数,它会在CompletableFuture
对象的结果完成或出现异常时,异步地执行这个函数,并返回一个新的CompletableFuture
对象,包含了函数的返回值。这个函数的第一个参数是一个任意类型的对象,表示结果的值,如果出现异常,这个值为null,第二个参数是一个Throwable
对象,表示异常的原因,如果没有异常,这个值为null,它的返回值是一个任意类型的对象,表示异常处理后的结果。这个方法主要用于对结果或异常进行统一的处理,比如转换,过滤,验证等。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 根据条件抛出一个异常
if (Math.random() > 0.5) {
throw new RuntimeException("Something went wrong");
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 处理结果或异常,转换为一个字符串
CompletableFuture<String> stringFuture = future.handle((result, exception) -> {
if (exception == null) {
return "The number is " + result;
} else {
return "The error is " + exception.getMessage();
}
});
// 获取结果,打印出来
try {
System.out.println(stringFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
5.超时问题
CompletableFuture
提供了一些方法来处理超时问题,这些方法都返回一个新的CompletableFuture
对象,包含了超时处理后的结果或异常。我们可以根据不同的场景选择合适的方法。
5.1 orTimeout()方法
orTimeout()
方法是一个实例方法,它接受两个参数,一个是一个long
类型的参数,表示超时的时间,另一个是一个TimeUnit
类型的参数,表示时间的单位,它会在CompletableFuture
对象的结果完成或超过指定的时间后,返回一个新的CompletableFuture
对象,包含了结果或异常。如果超过了指定的时间,这个对象会包含一个TimeoutException
异常,表示超时的原因。这个方法主要用于设置一个最大的等待时间,防止异步任务无限期地延迟。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 设置一个超时时间,为1秒
CompletableFuture<Integer> timeoutFuture = future.orTimeout(1, TimeUnit.SECONDS);
// 获取结果,打印出来
try {
System.out.println(timeoutFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
5.2 completeOnTimeout()方法
completeOnTimeout()
方法是一个实例方法,它接受三个参数,一个是一个任意类型的参数,表示一个默认的结果,另一个是一个long
类型的参数,表示超时的时间,最后一个是一个TimeUnit
类型的参数,表示时间的单位,它会在CompletableFuture
对象的结果完成或超过指定的时间后,返回一个新的CompletableFuture
对象,包含了结果或默认的结果。如果超过了指定的时间,这个对象会包含一个默认的结果,而不是一个异常。这个方法主要用于提供一个备选的结果,或者记录一个日志,或者抛出一个新的异常。例如:
// 创建一个异步任务,返回一个随机数
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回一个随机数
return new Random().nextInt(100);
});
// 设置一个超时时间,为1秒,和一个默认的结果,为0
CompletableFuture<Integer> defaultFuture = future.completeOnTimeout(0, 1, TimeUnit.SECONDS);
// 获取结果,打印出来
try {
System.out.println(defaultFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
6.总结
本文介绍了CompletableFuture
的多种创建方式,结果处理方法,异常处理方法,以及超时问题,希望能够帮助你更好地使用这个强大的类,让你的程序性能起飞。CompletableFuture
是Java 8中的一个重要的特性,它可以让我们更方便地编写异步的代码,提高程序的并发性和效率,也可以让我们的代码更简洁和优雅,避免了传统的Future
接口的一些缺点和限制。如果你想了解更多关于CompletableFuture
的内容,你可以参考以下的资源: