异步多线程-CompletableFuture

CompletableFuture 是 Java 8 引入的一种强大的工具,用于异步编程。它是 Future 的增强版,提供了很多有用的方法来简化异步编程。以下是一些 CompletableFuture 的常见使用场景和方法:

一、 创建 CompletableFuture

1.1. 手动完成

CompletableFuture<String> future = new CompletableFuture<>();
// 异步操作
new Thread(() -> {
    try {
        Thread.sleep(1000);
        future.complete("Hello");
    } catch (InterruptedException e) {
        future.completeExceptionally(e);
    }
}).start();

// 等待结果
System.out.println(future.get()); // 输出: Hello

1.2. 静态方法创建

CompletableFuture<String> completedFuture = CompletableFuture.completedFuture("Hello");
System.out.println(completedFuture.get()); // 输出: Hello

二、 异步计算

2.1. supplyAsync

用于异步执行带返回值的操作。

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Hello";
});
System.out.println(future.get()); // 输出: Hello

2.2. runAsync

用于异步执行不带返回值的操作。

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    try {
        Thread.sleep(1000);
        System.out.println("Task completed");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
future.get(); // 等待任务完成

三、获取结果的4种方式

  • get():这是一个阻塞方法,它会一直等待直到异步任务完成并返回结果。如果异步任务在完成前被取消了,它将抛出CancellationException;如果发生异常,它将抛出**ExecutionException
  • get(long timeout, TimeUnit unit):这个方法也是阻塞的,但它会等待指定的时间。如果在超时之前任务完成,它将返回结果。如果超时,它将抛出TimeoutException
  • join():这个方法类似于get(),但是它不会抛出检查异常,而是将异常包装在CompletionException
  • getNow(T valueIfAbsent):这个方法尝试立即获取结果,如果结果已经准备好了,就返回结果。否则,它返回传入的valueIfAbsent参数。

代码案例:

/**
     * 获取结果的4种方式
     * get():这是一个阻塞方法,它会一直等待直到异步任务完成并返回结果。如果异步任务在完成前被取消了,它将抛出CancellationException;如果发生异常,它将抛出ExecutionException。
     * get(long timeout, TimeUnit unit):这个方法也是阻塞的,但它会等待指定的时间。如果在超时之前任务完成,它将返回结果。如果超时,它将抛出TimeoutException。
     * join():这个方法类似于get(),但是它不会抛出检查异常,而是将异常包装在CompletionException中。
     * getNow(T valueIfAbsent):这个方法尝试立即获取结果,如果结果已经准备好了,就返回结果。否则,它返回传入的valueIfAbsent参数。
     */
    @Test
    public void testGetResultWay() throws Exception {
        long startTime = System.currentTimeMillis();

        CompletableFuture<String> uCompletableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "返回值";
        });

        // 立刻获取,拿不到,返回默认值
        System.out.println("第一种: " + uCompletableFuture.getNow("默认值"));
        // 阻塞获取
        System.out.println("第二种: " + uCompletableFuture.join());
        // 阻塞获取
        System.out.println("第三种: " + uCompletableFuture.get());
        // 设置指定时间内获取,获取不到会抛出异常
        System.out.println("第四种: " + uCompletableFuture.get(5, TimeUnit.MILLISECONDS));

        //模拟主程序耗时时间
        Thread.sleep(600);
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

四、 组合 CompletableFuture-异步回调

4.1. thenRun 和 thenRunAsync

用于在 CompletableFuture 完成后执行一个 Runnable,不使用计算结果。

    /**
     * 异步回调方法:
     */
    @Test
    public void testAsynchronousCallback() throws Exception {
        long startTime = System.currentTimeMillis();

        // 任务一:
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一完成");
            return "返回任务一完成";
        });

        // thenRun 和 thenRunAsync:
        //  -- 这两个方法在会在执行完第一个任务后,执行第二个任务,都没有返回值
        // thenRun 和 thenRunAsync的区别:区别在于线程池的复用
        // 1. thenRun(Runnable action):
        //  -- 这个方法在原始 CompletableFuture 完成后,使用原始 CompletableFuture 完成操作的线程(或者调用 join() 或 get() 的线程)来执行提供的 Runnable。
        //  -- 它不会创建新的线程来执行 Runnable。
        // 2. thenRunAsync(Runnable action):
        //  -- 这个方法在原始 CompletableFuture 完成后,可能会在一个新的线程中执行提供的 Runnable。
        //  -- 如果没有指定线程池,它将使用 ForkJoinPool.commonPool() 来执行 Runnable,这意味着它可能会在不同的线程中执行。
        //  -- 你也可以提供一个自定义的 Executor 来执行 Runnable,这样你就可以控制 Runnable 在哪个线程池中执行。
        CompletableFuture<Void> task2 = task1.thenRun(() -> {
            System.out.println("thenRun:任务二完成");
        });
        //模拟主程序耗时时间
        Thread.sleep(600);
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

4.2. thenAccept 和 thenAcceptAsync

用于消费 CompletableFuture 的结果,不返回新的 CompletableFuture

/**
     * 异步回调方法:
     */
    @Test
    public void testAsynchronousCallback() throws Exception {
        long startTime = System.currentTimeMillis();

        // 任务一:
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一完成");
            return "返回任务一完成";
        });
        // thenAccept 和 thenAcceptAsync:
        //  -- 这两个方法在会在执行完第一个任务后,执行第二个任务,并接收第一个任务的返回值,作为第二个任务中的入参并进行使用,而且第二个任务没有返回值
        // 区别:和thenRun 和 thenRunAsync的区别类似
        CompletableFuture<Void> task3 = task1.thenAccept((item) -> {
            System.out.println("thenAccept:上一个任务的返回值:" + item);
            System.out.println("thenAccept:任务二完成");
        });
        //模拟主程序耗时时间
        Thread.sleep(600);
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

4.3. thenApply 和 thenApplyAsync

用于将 CompletableFuture 的结果应用到函数,并返回一个新的 CompletableFuture

  /**
     * 异步回调方法:
     */
    @Test
    public void testAsynchronousCallback() throws Exception {
        long startTime = System.currentTimeMillis();

        // 任务一:
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一完成");
            return "返回任务一完成";
        });
        // thenApply 和 thenApplyAsync:
        //  -- 这两个方法在会在执行完第一个任务后,执行第二个任务,并接收第一个任务的返回值,作为第二个任务中的入参并进行使用,而且第二个任务可以有返回值
        // 区别:和thenRun 和 thenRunAsync的区别类似
        CompletableFuture<String> task4 = task1.thenApply((item) -> {
            System.out.println("thenApply:上一个任务的返回值:" + item);
            System.out.println("thenApply:任务二完成");
            return "任务二完成";
        });
        System.out.println("thenApply返回值:" + task4.get());

        //模拟主程序耗时时间
        Thread.sleep(600);
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

4.4. whenComplete + exceptionally

/**
     * 异步回调方法2:
     */
    @Test
    public void testAsynchronousCallback2() throws Exception {
        long startTime = System.currentTimeMillis();

        // 任务一:
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一完成");
            return "返回任务一完成";
        });

        CompletableFuture<String> completableFuture1 = task1.whenComplete((item, throwable) -> {
            System.out.println("whenComplete:上一个任务的返回值:" + item);
            try {
                throw new Exception("whenComplete:上一个任务抛出异常");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).exceptionally( throwable -> {
            return throwable.getMessage();
        });
        // 打印
        System.out.println("whenComplete返回值:" + completableFuture1.get());

        CompletableFuture<String> completableFuture2 = task1.handle((item, throwable) -> {
            System.out.println("handle:上一个任务的返回值:" + item);
            return "返回任务一完成";
        }).exceptionally( throwable -> {
            return "异常处理~";
        });
        System.out.println("handle返回值:" + completableFuture2.get());

        //模拟主程序耗时时间
        Thread.sleep(600);
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

五、多任务组合回调

5.1. runAfterBoth、thenAcceptBoth、thenCombine

/**
     * 多任务组合回调方法:
     */
    @Test
    public void testMultitaskCombinationCallback() throws Exception {
        long startTime = System.currentTimeMillis();

        // 任务一:
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一完成");
            return "返回任务一完成";
        });

        // 任务二:
        CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务二完成");
            return "返回任务二完成";
        });

        // -- runAfterBoth作用:无入参;无返回值
        CompletableFuture<Void> task3 = task1.runAfterBoth(task2, () -> {
            System.out.println("runAfterBoth:任务一和任务二都完成");
            System.out.println("runAfterBoth:执行任务三");
        });
        System.out.println("runAfterBoth返回值:" + task3.get());

        // -- thenAcceptBoth:有入参;无返回值
        CompletableFuture<Void> task4 = task1.thenAcceptBoth(task2, (item1, item2) -> {
            System.out.println("thenAcceptBoth:任务一和任务二都完成");
            System.out.println("thenAcceptBoth:任务一返回值:" + item1);
            System.out.println("thenAcceptBoth:任务二返回值:" + item2);
            System.out.println("thenAcceptBoth:执行任务三");
        });
        System.out.println("thenAcceptBoth:" + task4.get());

        // -- thenCombine:有入参;有返回值
        CompletableFuture<String> task5 = task1.thenCombine(task2, (item1, item2) -> {
            System.out.println("thenCombine:任务一和任务二都完成");
            System.out.println("thenCombine:任务一返回值:" + item1);
            System.out.println("thenCombine:任务二返回值:" + item2);
            System.out.println("thenCombine:执行任务三");
            return "返回任务三完成";
        });
        System.out.println("thenCombine:" + task5.get());

        //模拟主程序耗时时间
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

5.2. runAfterEither、acceptEither、applyToEither

/**
     * 多任务组合回调方法2:
     */
    @Test
    public void testMultitaskCombinationCallback2() throws Exception {
        long startTime = System.currentTimeMillis();

        // 任务一:
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务一完成");
            return "返回任务一完成";
        });

        // 任务二:
        CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("任务二完成");
            return "返回任务二完成";
        });

        // runAfterEither: 无入参;无返回值
        CompletableFuture<Void> task3 = task1.runAfterEither(task2, () -> {
            System.out.println("runAfterEither:任务一或任务二完成了");
            System.out.println("runAfterEither:执行任务三");
        });

        // acceptEither: 有入参;无返回值
        CompletableFuture<Void> task4 = task1.acceptEither(task2, (item) -> {
            System.out.println("acceptEither:任务一或任务二完成了,且返回值为:" + item);
            System.out.println("acceptEither:执行任务三");
        });

        // applyToEither: 有入参;有返回值
        CompletableFuture<String> task5 = task1.applyToEither(task2, (item) -> {
            System.out.println("applyToEither:任务一或任务二完成了,且返回值为:" + item);
            System.out.println("applyToEither:执行任务三");
            return "返回任务三完成";
        });
        System.out.println("applyToEither:" + task5.get());

        //模拟主程序耗时时间
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }

5.3. allOf

用于等待多个 CompletableFuture 全部完成。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");

CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2);
allOf.thenRun(() -> {
    try {
        System.out.println(future1.get() + " " + future2.get()); // 输出: Hello World
    } catch (Exception e) {
        e.printStackTrace();
    }
});

5.4. anyOf

用于等待多个 CompletableFuture 中的任意一个完成。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");

CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future1, future2);
System.out.println(anyOf.get()); // 输出: Hello 或 World

这些是 CompletableFuture 的一些基本用法,能够帮助你在 Java 中实现更加灵活和高效的异步编程。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CompletableFuture 是 Java 8 引入的一个类,用于实现多线程异步编程。它提供了丰富的方法来处理异步任务的结果、组合多个异步任务以及处理异常情况。 要实现超时处理,你可以结合 CompletableFuture 的方法和 Java 的 ScheduledExecutorService 来实现。下面是一个示例: ```java import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class CompletableFutureTimeoutExample { public static void main(String[] args) { // 创建一个 ScheduledExecutorService 用于超时处理 ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); // 创建一个 CompletableFuture 对象 CompletableFuture<String> future = new CompletableFuture<>(); // 设置超时时间 long timeout = 5000; // 5秒 // 在指定时间后取消 CompletableFuture executorService.schedule(() -> { if (!future.isDone()) { future.completeExceptionally(new TimeoutException("Timeout")); } }, timeout, TimeUnit.MILLISECONDS); // 异步任务 CompletableFuture.runAsync(() -> { try { // 执行异步操作,例如网络请求、数据库查询等 String result = performAsyncTask(); // 当任务完成时,设置结果给 CompletableFuture future.complete(result); } catch (Exception e) { future.completeExceptionally(e); } }); // 获取结果或处理超时异常 try { String result = future.get(); // 处理成功结果 } catch (TimeoutException e) { // 处理超时异常 } catch (InterruptedException | ExecutionException e) { // 处理其他异常 } // 关闭 ScheduledExecutorService executorService.shutdown(); } private static String performAsyncTask() { // 执行异步任务的逻辑 return "Async task result"; } } ``` 在上面的示例中,我们创建了一个 CompletableFuture 对象,并使用 ScheduledExecutorService 在指定的超时时间后取消 CompletableFuture。然后,我们使用 CompletableFuture.runAsync 方法执行异步任务,并在任务完成时设置结果给 CompletableFuture。最后,我们使用 future.get() 方法来获取结果或处理超时异常。 希望以上信息能够帮助到你!如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值