FutureValueHolder:自定义延迟加载异步Future

2 阅读3分钟

一、前言:异步编程中的痛点

在现代Java应用中,异步编程已成为提升系统性能的必备技能。Future接口让我们能够以非阻塞的方式处理耗时操作,但在实际使用中,容易遇到了几个棘手的痛点:

  1. 异常处理代码重复:每次调用future.get()都需要完整的try-catch块,代码冗余且难以维护
  2. 降级策略不统一:对于同一个异步结果,不同业务场景需要不同的降级策略
  3. 超时配置分散:同一异步操作在不同上下文中需要不同的超时设置
  4. 语义化不足Future本身只是一个"占位符",缺乏业务语义的表达能力

今天将介绍一个简洁而强大的工具类FutureValueHolder,它可以让异步编程变得更加优雅。

二、核心解析:双重检查锁的精妙设计

2.1 核心数据结构

public class FutureValueHolder<T> {
    private final Future<T> future;
    private final Long timeout;
    private final TimeUnit unit;
    
    private boolean invoked = false;
    private T value = null;
}
  • future: 包装的原始异步任务
  • timeout/unit: 超时控制参数
  • invoked: 标记是否已执行获取操作
  • value: 缓存的结果值

2.2 工厂方法模式

public static <T> FutureValueHolder<T> of(Future<T> future, Long timeout, TimeUnit unit) {
    return new FutureValueHolder<T>(future, timeout, unit);
}

使用静态工厂方法创建实例,提供更好的类型推断和API设计。

2.3 核心算法:双重检查锁

public T get() {
    if (invoked) {           // 第一次检查(无锁)
        return value;
    }
    synchronized (this) {    // 加锁
        if (invoked) {       // 第二次检查(有锁)
            return value;
        }
        // 实际获取逻辑
        value = future.get(timeout, unit);
        invoked = true;
    }
    return value;
}

双重检查锁的优势:

  • 性能优化:大多数情况无需加锁
  • 线程安全:确保多线程环境下正确执行
  • 单次执行future.get()只成功调用一次

2.4 两种获取策略

严格模式(get)

public T get() {
    // ... 双重检查锁逻辑
    try {
        value = future.get(timeout, unit);
    } catch (Exception e) {
        throw new FutureValueHolderGetException(e); // 抛出异常
    }
    invoked = true;
}

安全模式(safeGet)

非关键业务,允许降级处理

public T safeGet() {
    // ... 双重检查锁逻辑
    try {
        value = future.get(timeout, unit);
    } catch (Exception e) {
        // ...
    }
    invoked = true;
}

三、应用示范:真实场景下的威力

3.1 场景一:用户信息服务

public class UserService {
    public UserDetailDTO getUserDetail(Long userId) {
        FutureValueHolder<User> userHolder = FutureValueHolder.of(
            userDao.getUserAsync(userId), 3L, TimeUnit.SECONDS);
        FutureValueHolder<List<Order>> ordersHolder = FutureValueHolder.of(
            orderDao.getOrdersAsync(userId), 3L, TimeUnit.SECONDS);
        
        // 第一次获取(可能等待)
        User user = userHolder.get();
        List<Order> orders = ordersHolder.get();
        
        // 后续访问(零等待)
        String name = userHolder.get().getName();     // 立即返回
        Integer age = userHolder.get().getAge();      // 立即返回
        String email = userHolder.get().getEmail();   // 立即返回
        
        return buildDTO(userHolder.get(), ordersHolder.get());
    }
}

3.2 场景二:外部API调用降级

传统写法(冗长的异常处理)

public class WeatherService {
    public WeatherInfo getWeather(String city) {
        Future<WeatherInfo> future = weatherApi.getAsync(city);
        
        try {
            return future.get(5, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            log.error("天气API超时", e);
            return WeatherInfo.defaultInfo();
        } catch (ExecutionException e) {
            log.error("天气API执行异常", e);
            return WeatherInfo.defaultInfo();
        } catch (InterruptedException e) {
            log.error("天气API被中断", e);
            return WeatherInfo.defaultInfo();
        }
    }
}

使用FutureValueHolder(简洁优雅)

public class WeatherService {
    public WeatherInfo getWeather(String city) {
        FutureValueHolder<WeatherInfo> holder = FutureValueHolder.of(
            weatherApi.getAsync(city), 5L, TimeUnit.SECONDS);
        
        // 简洁明了
        WeatherInfo info = holder.safeGet();
        return info != null ? info : WeatherInfo.defaultInfo();
    }
}

四、完整代码

public class FutureValueHolder<T> {  
  
    private final Future<T> future;  
    private final Long timeout;  
    private final TimeUnit unit;  
    private boolean invoked = false;  
    private T value = null;  
  
    private FutureValueHolder(Future<T> future, Long timeout, TimeUnit unit) {  
        this.future = future;  
        this.timeout = timeout;  
        this.unit = unit;  
    }  
  
    public static <T> FutureValueHolder<T> of(Future<T> future, Long timeout, TimeUnit unit) {  
        return new FutureValueHolder<T>(future, timeout, unit);  
    }  
  
    public T get() {  
        if (invoked) {  
            return value;  
        }  
        synchronized (this) {  
            if (invoked) {  
                return value;  
            }  
            try {  
                value = future.get(timeout, unit);  
            } catch (Exception e) {  
                throw new FutureValueHolderException(e);  
            }  
            invoked = true;  
        }  
        return value;  
    }  
  
    public T safeGet() {  
        if (invoked) {  
            return value;  
        }  
        synchronized (this) {  
            if (invoked) {  
                return value;  
            }  
            try {  
                value = future.get(timeout, unit);  
            } catch (Exception e) {  
                // ...
            }  
            invoked = true;  
        }  
        return value;  
    }  
  
}