简短结论:OkHttp 默认把异步请求的执行器配成**“缓存线程池”语义**
corePoolSize=0、maximumPoolSize=Integer.MAX_VALUE、keepAlive=60s、workQueue=SynchronousQueue,是为了低延迟、突发可扩,并把真正的并发上限交给 Dispatcher 去管控(默认 maxRequests=64、maxRequestsPerHost=5)。这样不会在执行器里排队,要么立刻有线程执行,要么就被 Dispatcher 按策略等待。
每个参数“为什么”
-
corePoolSize = 0:空闲时不占线程,App/进程空闲更省资源;有任务再建线程。
-
maximumPoolSize = Integer.MAX_VALUE + SynchronousQueue:执行器不排队(零容量队列),如果允许执行就直接移交给工作线程;没空闲线程就立刻新建一个(受 Dispatcher 的并发阈值约束)。这样避免长队列导致的新老请求“排队饿死”,更适合大量阻塞型 I/O。
-
keepAlive = 60s:应对突发流量时可以临时扩容线程;峰值过后 60s 回落,避免长时间占用资源。
关键点:真正的并发上限在 Dispatcher
OkHttp 不靠线程池大小限流,而是用 Dispatcher 的策略:
-
全局并发默认 64、每个主机默认 5;超过就在内存等待队列里排队(不是交给线程池排队)。这保证了低排队延迟 + 明确的网络并发上限。
-
你也可以自定义 ExecutorService 交给 Dispatcher 使用。
-
注意:Dispatcher 的这些上限不作用于同步请求(同步是你自己提供线程)。
为什么适合网络 I/O
网络调用大多是阻塞等待(读/写 socket),线程挂起的时间远大于 CPU 计算时间。用“直接移交 + 快速扩容”的执行器,能减少新请求被旧请求队列阻塞的概率;而整体吞吐与公平性交给 Dispatcher(再配合 HTTP/2 多路复用、连接池)。
风险与实践
-
滥改并发上限可能“线程爆炸” :如果把 Dispatcher 的限额调太大,配合 SynchronousQueue 会在短时间创建非常多线程,历史上在 Android 7.0 等平台有过 OOM/线程上限问题的反馈。务必按服务器与端侧能力谨慎调参。
-
若你的场景需要明确的上游背压/排队策略(而不是无限扩线程),可自定义:
-
有界线程池(固定最大线程 + LinkedBlockingQueue);或
-
仍用 SynchronousQueue 但降低 Dispatcher.maxRequests/PerHost;或
-
在业务层加 Semaphore/令牌桶 控制提交速度。
-
小结:OkHttp 选择“执行器不排队、由 Dispatcher 限流”的设计,使异步请求在 I/O 密集场景下获得更好的延迟与弹性,同时保持默认并发安全可控。