并发编程

Java × Python · 线程、协程与场景选择

并发是多个任务交替推进,并行是多个任务同时执行。等待网络、数据库属于 I/O 密集;压缩、纯代码计算属于 CPU 密集。先分清任务,再选工具。

Java:线程与并发工具

  • 线程池:用 ExecutorService 管理任务和线程,Future 获取结果;CompletableFuture 适合组合异步步骤。
  • 共享数据:用 synchronized / Lock 保护临界区,简单计数可用 AtomicIntegervolatile 保证可见性与相关顺序约束,不能让 count++ 变成原子操作。
  • 虚拟线程(Java 21+):适合大量阻塞 I/O 任务,保留同步写法;通常一个任务一个虚拟线程,无需再池化。它不会让 CPU 计算本身更快,下游连接数仍需限制。

参考:Java 并发工具 · 虚拟线程

Python:线程与协程

  • 线程ThreadPoolExecutor 适合调用阻塞式 I/O 库。默认启用 GIL 的 CPython 中,多线程通常不能加速纯 Python 的 CPU 计算;共享数据仍需加锁,GIL 不等于业务线程安全。
  • 协程async def 定义协程,asyncio 调度任务。等待未就绪的异步操作时,通过 await 让其他任务运行;单个事件循环里的任务并不同时执行 Python 代码。
  • 别阻塞事件循环:协程中使用异步客户端和 await asyncio.sleep();阻塞 I/O 可交给 asyncio.to_thread(),CPU 重任务考虑进程池。

版本说明:Python 3.13+ 提供可选的自由线程构建,可关闭 GIL;能否受益还取决于构建方式和扩展库兼容性。下面对比按常见的 GIL 开启模式说明。

参考:Python 线程 · 协程与任务 · 自由线程构建

同一个例子:同时等待两个任务

三个例子都用等待 1 秒模拟 I/O,返回 AB。任务先一起提交,再等待结果,因此总耗时通常约 1 秒,而非串行的约 2 秒;这不是性能基准。

Java 线程池 · Java 21+

保存为 ConcurrencyDemo.java,执行 java ConcurrencyDemo.java

import java.util.concurrent.Executors;

public class ConcurrencyDemo {
    static String work(String name) throws InterruptedException {
        Thread.sleep(1000);
        return name;
    }

    public static void main(String[] args) throws Exception {
        try (var pool = Executors.newFixedThreadPool(2)) {
            var a = pool.submit(() -> work("A"));
            var b = pool.submit(() -> work("B"));
            System.out.println(a.get() + ", " + b.get());
        }
    }
}

体验虚拟线程:将 newFixedThreadPool(2) 换成 newVirtualThreadPerTaskExecutor(),其余代码不变。

Python 线程池 · Python 3.11+

import time
from concurrent.futures import ThreadPoolExecutor

def work(name):
    time.sleep(1)
    return name

with ThreadPoolExecutor(max_workers=2) as pool:
    print(list(pool.map(work, ["A", "B"])))

Python 协程 · Python 3.11+

import asyncio

async def work(name):
    await asyncio.sleep(1)
    return name

async def main():
    async with asyncio.TaskGroup() as group:
        a = group.create_task(work("A"))
        b = group.create_task(work("B"))
    print([a.result(), b.result()])

asyncio.run(main())

TaskGroup 退出时等待组内任务;某个任务出现非取消异常时,会取消其余任务并汇总异常。连续写 await work("A")await work("B") 则会串行等待。

Java 与 Python,怎么选?

场景 / 特性JavaPython
阻塞式 I/O线程池;大量任务可用虚拟线程线程池;已有异步应用可用 to_thread 接入
大量异步 I/O虚拟线程,或异步客户端asyncio + 异步客户端
CPU 密集受控大小的线程池,可利用多核纯 Python 优先考虑进程池;释放 GIL 的原生库另论
共享状态synchronizedLock、原子类线程用 threading.Lock;同一事件循环的协程用 asyncio.Lock
调度方式平台线程由操作系统调度;虚拟线程由 JVM 调度线程由操作系统调度;协程由事件循环协作调度

虚拟线程与协程的关键区别:Java 虚拟线程仍使用线程 API,许多 JDK 阻塞操作可由运行时挂起;Python 协程需要显式的 async / await 和异步库配合。二者都适合提高等待型任务的吞吐,不能直接等同。

放到 Agent 项目里:并发调用模型或工具,多数时间在等待网络,优先考虑 I/O 并发;本地重计算单独交给进程或计算服务。实际业务还要设置超时、限制并发数、处理取消,避免压垮下游。

参考:Python 线程池与进程池