异步神器:Java 8 CompletableFuture大揭秘,程序性能爆炸提升

1.简介

在Java中,我们经常需要处理异步的任务,比如调用远程服务,执行耗时的计算,或者等待用户的输入。传统的方法是使用Future接口,它表示一个未完成的操作,可以通过get()方法获取结果,或者通过isDone()方法检查是否完成。但是,Future接口有一些缺点,比如:

  • get()方法是阻塞的,会导致当前线程等待,浪费资源和时间。
  • Future接口没有提供任何方法来处理结果或异常,我们只能手动编写回调逻辑,代码复杂和冗余。
  • Future接口不支持组合多个异步任务,我们只能嵌套多层Future,代码可读性和维护性差。

为了解决这些问题,Java 8引入了一个新的类,叫做CompletableFuture,它实现了Future接口,但是提供了更多的功能和灵活性,比如:

  • CompletableFuture可以通过静态方法或者构造方法创建,可以指定异步任务的执行器,也可以使用默认的ForkJoinPool.commonPool()
  • CompletableFuture可以通过一系列的方法来处理结果或异常,比如thenApplythenAcceptthenComposethenCombine等,这些方法返回一个新的CompletableFuture,形成了一个流式的编程风格。
  • CompletableFuture可以通过allOfanyOf等方法来组合多个异步任务,可以实现并行或者并发的效果,提高程序的性能和效率。

本文将介绍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对象,我们可以通过completecompleteExceptionally等方法来手动地设置这个对象的结果或异常。这个方法主要用于自定义异步任务的执行逻辑。例如:

// 创建一个空的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的内容,你可以参考以下的资源:

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值