Java并发编程中的并发库与工具类介绍

java.util.concurrent包的核心定位是提供可组合、线程安全、有明确语义的并发构建块,而非synchronized或wait/notify的

语法糖;典型工具如ArrayBlockingQueue、ForkJoinPool等直接解决生产者-消费者、任务分治等场景。

java.util.concurrent 包的核心定位是什么

它不是用来替代 synchronizedwait/notify 的“高级语法糖”,而是为解决典型并发场景提供**可组合、线程安全、有明确语义**的构建块。比如你不需要手写一个线程安全的生产者-消费者队列,直接用 ArrayBlockingQueue 就行;也不必自己实现任务拆分合并逻辑,ForkJoinPool 已经封装好。

哪些工具类最容易误用

常见错误不是不会用,而是没看清行为边界:

  • ConcurrentHashMapsize() 返回的是近似值,高并发下可能滞后 —— 需要精确计数请改用 LongAdder
  • CopyOnWriteArrayList 适合读多写极少的场景(如监听器列表),但每次写操作都复制整个数组,写频繁时 GC 压力大、内存占用高
  • CountDownLatch 只能触发一次,想重复使用得换 CyclicBarrier 或重置逻辑
  • ExecutorService.submit(Runnable) 返回的 Future 若不调用 get()isDone(),异常会静默丢失

ThreadPoolExecutor 的关键参数怎么配

不是所有线程池都该设成 newFixedThreadPool(10)。真实系统中需权衡阻塞比例、响应延迟与资源消耗:

  • corePoolSize:常驻线程数,建议设为 CPU 核心数 × (1 + 平均阻塞系数),比如 DB 调用多就往高调
  • maximumPoolSize:仅当 workQueue 满且仍有新任务时才创建额外线程,设太大易引发上下文切换风暴
  • keepAliveTime:对非核心线程有效,IO 密集型服务可设长些(如 60s),避免频繁启停
  • workQueue 别无脑选 LinkedBlockingQueue —— 它默认无界,OOM 风险高;优先考虑 ArrayBlockingQueue 或带拒绝策略的 SynchronousQueue

CompletableFuture 和传统 Future 本质区别在哪

根本不在“异步”,而在**可编排性**:Future 是被动等待,CompletableFuture 支持链式回调、组合依赖、异常处理分流:

CompletableFuture.supplyAsync(() -> fetchFromDB())
    .thenCompose(data -> CompletableFuture.supplyAsync(() -> callRemoteAPI(data)))
    .exceptionally(ex -> fallbackValue())
    .thenAccept(result -> log.info("done: {}", result));

注意点:

  • 默认使用 ForkJoinPool.commonPool(),CPU 密集型任务建议显式传入专用线程池,避免拖慢其他模块
  • thenApply / thenAccept 等方法在前序阶段完成时同步执行,若耗时长会阻塞线程池 —— 此时应改用 thenApplyAsync
  • join() 不抛检查异常,但若上游异常未处理,调用 join() 会直接抛 CompletionException,包装了原始异常

并发库的复杂度不在 API 数量,而在每个类背后隐含的线程模型假设和状态流转规则。用错一个队列或一个拒绝策略,可能让系统在压测时突然卡死,而不是报错——这种问题往往要靠日志和线程 dump 才能定位。