MST

星途 面试题库

面试题:Java中CompletableFuture的get方法阻塞场景分析

请描述在哪些常见场景下,使用CompletableFuture的get方法会导致线程阻塞,并说明如何通过代码示例来模拟这些场景,同时阐述如何避免因get方法阻塞而造成的性能问题。
19.6万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

线程阻塞场景

  1. 任务未完成时调用get:当CompletableFuture所代表的异步任务尚未完成,此时调用get方法,调用线程会一直阻塞,直到任务完成。例如,在一个复杂的计算任务,或者需要远程调用且网络延迟较大的任务场景下,任务执行时间较长,如果在任务未结束时就调用get,就会发生阻塞。
  2. 任务执行过程中出现异常:若CompletableFuture在执行任务过程中抛出异常,调用get方法获取结果时,线程也会阻塞,直到异常被处理。异常处理机制会使get方法等待,直到明确异常情况并处理完成。

代码模拟

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureExample {
    public static void main(String[] args) {
        // 模拟任务未完成时调用get
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(5000); // 模拟长时间运行的任务
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Task 1 completed";
        });

        try {
            System.out.println("Before get, trying to get result: " + future1.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        // 模拟任务执行过程中出现异常
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            throw new RuntimeException("Task 2 error");
        });

        try {
            System.out.println("Before get, trying to get result: " + future2.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

避免性能问题

  1. 使用异步回调:通过thenApplythenAcceptthenRun等方法注册回调函数,这些回调会在任务完成时异步执行,不会阻塞主线程。
CompletableFuture.supplyAsync(() -> "Hello")
               .thenApply(s -> s + ", World")
               .thenAccept(System.out::println);
  1. 设置超时:使用get(long timeout, TimeUnit unit)方法设置获取结果的超时时间,避免无限期阻塞。
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Task completed";
});

try {
    String result = future.get(2, TimeUnit.SECONDS);
    System.out.println(result);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
    e.printStackTrace();
}
  1. 结合CompletableFuture的组合操作:利用allOfanyOf等方法处理多个CompletableFuture,合理安排任务执行和结果获取逻辑,减少阻塞时间。
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Result 1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result 2");

CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2);
combinedFuture.thenRun(() -> {
    try {
        System.out.println("Future 1 result: " + future1.get());
        System.out.println("Future 2 result: " + future2.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}).join();