Java中的并发模型与并行处理

Java并发模型以共享内存和显式同步为核心,依赖synchronized、volatile及JUC工具保障线程安全;并行能力由ForkJoinPool、parallelStream等提供,但需权衡任务性质与资源开销。

Java 的并发模型不是“并行优先”,而是以线程协作与状态管理为核心;真正决定是否并行的,是任务性质、硬件资源和执行器配置,而非语言模型本身。

Java 并发模型本质是共享内存 + 显式同步

Java 的 java.lang.Threadsynchronizedvolatilejava.util.concurrent 工具集,全部建立在 JVM 级别的共享堆内存之上。这意味着:

  • 所有线程默认可读写同一对象,但不保证可见性或原子性 —— 必须用 synchronizedLockAtomicInteger 等显式控制
  • volatile 只解决可见性,不解决复合操作(如 i++)的原子性
  • JVM 不自动做数据竞争检测,写错同步逻辑会导致极难复现的偶发错误,比如 ConcurrentModificationException 或静默的数据错乱

并行处理靠的是 ForkJoinPoolparallelStream(),不是普通线程池

普通 ThreadPoolExecutor(包括 Executors.newFixedThreadPool())只负责并发调度,不提供工作窃取或自动拆分能力。真正支持 CPU 密集型并行任务的,是:

  • ForkJoinPool.commonPool()parallelStream()CompletableFuture.supplyAsync() 默认使用它
  • 并行度默认 = Runtime.getRuntime().availableProcessors() - 1(避免占用全部核心影响系统响应)
  • 若任务含阻塞 I/O(如数据库调用),parallelStream() 会拖慢整体吞吐——此时应显式创建专用线程池:
    new ForkJoinPool(4)

CompletableFuture 是组合异步逻辑的事实标准,但别滥用 join()

它比传统 Future 更适合构建非阻塞流水线,但常见误用是过早调用 join() 导致线程阻塞:

  • thenApply()thenCompose() 是非阻塞的链式回调;join() 是同步等待,会卡住当前线程
  • 在 Servlet 容器(如 Tomcat)中,从 Servlet.service() 内直接 join() 可能耗尽线程池
  • 需要等多个结果时,优先用 CompletableFuture.allOf()CompletableFuture.anyOf(),而非循环 join()

并行 ≠ 自动加速。数组排序用 Arrays.parallelSort() 可能快几倍,但对小集合或含锁逻辑的任务,启动并行开销反而更高。关键不是“能不能并行”,而是“

值不值得并行”——这得看数据规模、CPU 利用率、以及有没有隐藏的共享状态争用。