Java 使用 Future 实现异步操作

431 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情

1. Future

Future 是 JDK 1.5 在 java.util.concurrent 包引入的新接口,用来获取异步线程执行的结果。

Future 常和 Callable 接口一起使用,用来将线程的执行状态交给 Future,并通过 Future 获取返回结果。

Callable 接口与 Rnnable 接口类似,都是 Java 中的函数式接口,并且可以作为参数来实现线程逻辑处理

ExecutorService executorService = Executors.newFixedThreadPool(1);
Future<String> future = executorService.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        return "Hello, World!";
    }
});

2. Future 源码定义

Future 接口的源码内容还是比较简单的,只定义了少几个接口方法。

  • 接口定义
public interface Future<V> {
    ...
}

2.1 cancel() 方法

boolean cancel(boolean mayInterruptIfRunning);
  • cancel() 方法允许取消 Future 异步操作,传入布尔值参数用来指定异步线程执行过程中是否允许打断

  • 返回异步操作是否取消成功的结果,一般当异步操作已经执行完成时,返回结果为 false;

  • 当传入参数为 false 时,代表不会打断异步线程操作,等待操作完成后,方法返回结果也是 false。

2.2 isCanceled() 方法

boolean isCancelled();
  • 方法返回 Future 异步操作是否在正常完成之前被取消掉,是则返回 true,否则返回 false
  • 如果异步线程未执行前调用了 cancel() 方法,或者打断了异步操作执行,则返回 false

2.3 isDone()

boolean isDone();
  • isDone() 方法返回 Future 异步操作是否已经完成

2.4 get() 方法

无参 get() 方法

V get() throws InterruptedException, ExecutionException;
  • 无参的 get() 方法用来获取 Future 异步线程操作的结果,如果线程取消、中断会抛出对应异常信息,否则会一直等待异步线程完成后返回结果

  • 需要注意的是,在主线程中获取 Future 结果时,当前线程会被阻塞!直到得到返回结果后继续执行后续逻辑

有参 get(long timeout, TimeUnit unit) 方法

V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
  • 带参数的 get(long timeout, TimeUnit unit) 方法可以传入时间数值和单位参数,代表获取 Future 结果时最大等待的时间,如果时间超过最大值且没有结果时,会抛出超时异常。

  • 带参数的方法在获取到返回结果或者抛出异常之前同样是阻塞当前线程的,使用时需要注意

3. Future 使用

3.1 定义方法测试 Future 阻塞性

@Test
void testFuture(){
    // Future
    ExecutorService executorService = Executors.newFixedThreadPool(1);
    Future<String> future = executorService.submit(() -> {
        System.out.println("异步线程开始,name:" + Thread.currentThread().getName());
        try {
            // Thread.sleep(1000);
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("异步线程结束,name:" + Thread.currentThread().getName());
        return "Hello, World!";
    });

    String futureResult = future.get(1,TimeUnit.SECONDS);
    System.out.println("异步线程结果:" + futureResult);
    System.out.println("主线程:" + Thread.currentThread().getName());
    Thread.sleep(10000);

    System.out.println("主线程结束,name:" + Thread.currentThread().getName());
}

3.2 @Async

@Async 注解是 Spring 提供的异步注解,在 Spring 环境中,只需要在方法上标注 @Async 注解,当调用容器中对象的该方法时,便会以异步的方式完成方法执行。

  • 需要注意的式, @Async 注解标注的方法一定要写在 Spring 管理的类中

  • 另外,调用注解 @Async 标注的方法一定是要从外部调用,而不能由同类中方法调用,内部调用时无法使用 Spring 的 AOP 使注解生效

@Async
public void asyncOperate(){
    System.out.println("异步操作");
}

@Async
public Future asyncOperate(){
    System.out.println("异步操作");
    return "返回结果!";
}

Future<String> future = asyncOperate();
// 获取执行结果,阻塞当前线程
String result = future.get();
  • 其实 @Async 注解只是 Spring 在原生的 Future 中进行了一次封装,最终获得的还是 Future 实例,不过在 Spring 环境中使用更加方便快捷