一、Future详解

1.future是什么

future是对异步执行结果的一种抽象,本质是:一个占位符,代表将来某一时刻可以拿到的结果

Future<V>

提交一个任务,线程池立刻返回future,任务在后台执行

2.future的核心能力

方法 含义
get() 阻塞等待结果
get(timeout) 超时等待
cancel() 尝试取消任务
isDone() 是否完成
isCancelled() 是否被取消

3.future的致命缺陷

(1)无法非阻塞获取结果

future.get(); // 必须阻塞

(2)无法链式编排:多个异步任务无法优雅组合
(3)无法回调通知:任务完成后,不能主动通知

二、CompletableFuture 执行异步任务

1.CompletableFuture 的定位

Future + Callback + Stream + DAG(任务图)

2.创建异步任务的三种方式

2.1 无返回值任务(Runnable)

CompletableFuture.runAsync(() -> {
    System.out.println("异步任务执行");
});

2.2 有返回值任务(Supplier)

CompletableFuture<Integer> future =
    CompletableFuture.supplyAsync(() -> {
        return 100;
    });

2.3 使用自定义线程池

ExecutorService executor = Executors.newFixedThreadPool(10);

CompletableFuture<Integer> future =
    CompletableFuture.supplyAsync(() -> {
        return 100;
    }, executor);

默认使用 ForkJoinPool.commonPool(),不适合 IO 密集业务

三、CompletableFuture 结果处理

1.同步处理(thenApply)

future.thenApply(result -> result * 2);
  • 有返回值
  • 不切线程
  • 可能阻塞当前线程

2.异步处理(thenApplyAsync)

future.thenApplyAsync(result -> result * 2);
  • 会切线程
  • 可指定线程池

3.消费结果(thenAccept)

future.thenAccept(result -> {
    System.out.println(result);
});

4.无关结果(thenRun)

future.thenRun(() -> {
    System.out.println("任务完成");
});

5.异常处理

5.1 exceptionally(兜底)

future.exceptionally(ex -> {
    log.error("异常", ex);
    return -1;
});

5.2 handle(结果 + 异常)

future.handle((result, ex) -> {
    if (ex != null) return -1;
    return result;
});

exceptionally 只处理异常
handle 无论成功失败都会执行

四、CompletableFuture 任务交互

1.顺序依赖(thenCompose)

CompletableFuture<Integer> f =
    CompletableFuture.supplyAsync(() -> 10)
        .thenCompose(r ->
            CompletableFuture.supplyAsync(() -> r * 2)
        );

上一个结果作为下一个任务的输入(flatMap)

2.并行任务合并(thenCombine)

CompletableFuture<Integer> f1 = ...
CompletableFuture<Integer> f2 = ...

f1.thenCombine(f2, Integer::sum);

3.任意完成(applyToEither)

f1.applyToEither(f2, r -> r);

4.等待所有任务

CompletableFuture.allOf(f1, f2).join();

五、ThreadLocal 的使用

1.ThreadLocal 是什么

为每个线程提供一份变量副本

ThreadLocal<String> tl = new ThreadLocal<>();
tl.set("userId");

以空间换线程安全

2.典型使用场景

  • 用户上下文(userID,token)
  • 数据源切换
  • traceID / 日志链路追踪
  • 事务上下文

六、ThreadLocal vs synchronized

维度 ThreadLocal synchronized
作用 线程隔离 线程互斥
是否加锁 不加锁 加锁
性能 相对低
数据共享 不共享 共享
使用场景 上下文 临界区

ThreadLocal 解决“每个线程一份数据”
synchronized 解决“多个线程抢一份数据”

七、ThreadLocal 内部结构详解

1.核心结构关系

Thread
 └── ThreadLocalMap
       └── Entry[] table
             ├── Entry(ThreadLocal, value)

ThreadLocalMap 不在 ThreadLocal 里,而是在 Thread 对象里

2.Entry 结构

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
}

📌 Key:弱引用 ThreadLocal
📌 Value:强引用

八、ThreadLocal 核心方法源码分析

1.set()

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = t.threadLocals;
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

2.get()

Entry e = map.getEntry(this);
return (T) e.value;

3.remove()

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

用完必须 remove(),尤其是线程池场景

九、ThreadLocal 内存泄漏原因分析

1.泄漏本质:Key 被 GC,Value 仍然被线程强引用

Thread
 └── ThreadLocalMap
       └── Entry
             ├── key = null
             └── value = 大对象

2.为什么线程池更危险

  • 线程不会销毁
  • Entry 长期存在
  • value 无法释放

3.如何避免

必须调用 remove()

try {
    tl.set(x);
} finally {
    tl.remove();
}

十、ThreadLocal 如何解决 Hash 冲突

1.底层是数组 + 开放寻址法

Entry[] table;

hash计算

int index = threadLocalHashCode & (len - 1);

2.冲突处理策略

index = nextIndex(index, len);

3.过期 Entry 清理机制

  • set / get / remove 时触发
  • 清理 key = null 的 Entry
  • 非实时,惰性清理

总结

1.为什么 CompletableFuture 比 Future 强?

  • 支持回调
  • 支持任务编排
  • 支持异常链路处理
  • 支持非阻塞

2.thenApply 和 thenCompose 区别?

  • thenApply:map
  • thenCompose:flatMap,解决嵌套 Future

3.ThreadLocal 为什么会内存泄漏?

  • Key 是弱引用
  • Value 是强引用
  • 线程长期存活(线程池)

4.ThreadLocal 和 synchronized 的本质区别?

  • ThreadLocal:数据隔离
  • synchronized:访问互斥

5.ThreadLocal 用在哪些生产场景?

  • 用户上下文
  • 日志 TraceId
  • 数据源切换
  • 事务上下文
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐