并发编程
Java × Python · 线程、协程与场景选择
并发是多个任务交替推进,并行是多个任务同时执行。等待网络、数据库属于 I/O 密集;压缩、纯代码计算属于 CPU 密集。先分清任务,再选工具。
Java:线程与并发工具
- 线程池:用
ExecutorService管理任务和线程,Future获取结果;CompletableFuture适合组合异步步骤。 - 共享数据:用
synchronized/Lock保护临界区,简单计数可用AtomicInteger。volatile保证可见性与相关顺序约束,不能让count++变成原子操作。 - 虚拟线程(Java 21+):适合大量阻塞 I/O 任务,保留同步写法;通常一个任务一个虚拟线程,无需再池化。它不会让 CPU 计算本身更快,下游连接数仍需限制。
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 开启模式说明。
同一个例子:同时等待两个任务
三个例子都用等待 1 秒模拟 I/O,返回 A、B。任务先一起提交,再等待结果,因此总耗时通常约 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,怎么选?
| 场景 / 特性 | Java | Python |
|---|---|---|
| 阻塞式 I/O | 线程池;大量任务可用虚拟线程 | 线程池;已有异步应用可用 to_thread 接入 |
| 大量异步 I/O | 虚拟线程,或异步客户端 | asyncio + 异步客户端 |
| CPU 密集 | 受控大小的线程池,可利用多核 | 纯 Python 优先考虑进程池;释放 GIL 的原生库另论 |
| 共享状态 | synchronized、Lock、原子类 | 线程用 threading.Lock;同一事件循环的协程用 asyncio.Lock |
| 调度方式 | 平台线程由操作系统调度;虚拟线程由 JVM 调度 | 线程由操作系统调度;协程由事件循环协作调度 |
虚拟线程与协程的关键区别:Java 虚拟线程仍使用线程 API,许多 JDK 阻塞操作可由运行时挂起;Python 协程需要显式的 async / await 和异步库配合。二者都适合提高等待型任务的吞吐,不能直接等同。
放到 Agent 项目里:并发调用模型或工具,多数时间在等待网络,优先考虑 I/O 并发;本地重计算单独交给进程或计算服务。实际业务还要设置超时、限制并发数、处理取消,避免压垮下游。