semaphore实现连接池本质是并发准入控制,仅限制最大活跃连接数,不管理连接创建、心跳或回收;借连接须先acquire再取connection,还连接须先归还connection再release,且必须严格配对并置于try-finally中。

Java 中用 Semaphore 实现连接池资源管理,核心不是替代 HikariCP 这类专业池,而是用轻量机制守住并发上限——它不管连接怎么建、是否有效、有没有超时,只管“此刻最多几个线程能拿走连接”。关键在许可与连接解耦、借还逻辑配对、异常兜底到位。
许可数要贴合真实资源承载力
设 10 个许可,不等于系统就能稳扛 10 并发。得看数据库连接池本身最大支持多少连接、单次操作平均耗时、QPS 峰值。比如连接池上限 20,业务请求平均耗时 800ms,QPS 峰值 50,理论并发约 40,但直接设 40 会挤占全部资源。通常取连接池上限的 70%~80%,即设 14~16 个许可更稳妥。避免凭感觉设 5 或 10,也别让许可数超过实际可用连接数,否则线程抢到许可却执行失败(如连接超时、OOM)。
借连接必须先 acquire 再取实例
顺序不能错:必须先调用 tryAcquire(timeout, unit) 获取许可,成功后再从线程安全队列(如 ConcurrentLinkedQueue)中取出 Connection。不能反过来——先取连接再 acquire,会导致许可和连接不匹配,池状态混乱。
- 禁用无参 acquire(),防止无限阻塞;生产环境统一用 tryAcquire(1, TimeUnit.SECONDS),超时返回 null 或抛定制异常
- 超时值不宜过短(如 10ms),应略大于空闲连接平均等待时间,可通过日志统计队列出队耗时来估算
- 若需响应中断(如服务优雅关闭),可用 acquireInterruptibly(),但别用 acquireUninterruptibly()
还连接必须先归还实例再 release
释放流程同样严格:先把 Connection 放回空闲队列,再调用 semaphore.release()。这两步必须包裹在同一个 try-finally 块里,哪怕 close() 抛异常也不能跳过 release。
- 常见错误是只放回连接、忘了 release,导致许可永久丢失,池逐渐“锁死”
- 禁止在 catch 块里 release —— 那是重复释放,会让许可数超过初始值,破坏限流效果
- 即使连接已关闭或失效,仍要 release;可额外做 ping 检查,无效则丢弃并新建替换
公平性与监控不能忽略
构造 Semaphore 时第二个参数决定排队行为:设为 true 是公平模式(FIFO),适合金融扣款等强顺序场景;默认 false(非公平)吞吐更高,适合后台任务等对顺序无要求的场景。同时,要通过 availablePermits() 定期采样,结合 Metrics 暴露当前空闲许可数,便于运维及时发现池压满或泄漏迹象。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











