druid核心路径已采用无锁cas、分段锁、读写锁等高效机制,滥用synchronized会引发全局串行瓶颈、死锁风险,违背其轻量高并发设计初衷。

在基于 Druid 连接池做二次开发时,synchronized 的加锁不是直接用于连接池核心逻辑的推荐做法,而应谨慎评估场景、粒度与替代方案。
为什么不宜在 Druid 核心路径上滥用 synchronized
Druid 本身已高度并发优化:连接获取、归还、心跳检测、监控统计等关键路径均采用无锁(CAS)、分段锁(如 ConcurrentHashMap)、读写锁(ReentrantReadWriteLock)或线程局部缓存(如 PooledConnection 的持有线程绑定)等高效机制。若在这些路径外擅自加 synchronized:
- 容易造成全局串行瓶颈,吞吐量骤降(尤其高并发获取连接时);
- 可能与 Druid 内部锁嵌套,引发死锁(例如:你锁住自定义对象,同时内部调用
DruidDataSource.getConnection()又需竞争连接池锁); - 违背连接池“轻量、快速、可伸缩”的设计初衷。
真正需要加锁的典型二次开发场景
仅当扩展逻辑引入了新的共享可变状态,且该状态被多线程并发访问时,才需考虑同步。常见情形包括:
-
自定义连接监控聚合器:比如统计每个 SQL 模板的平均耗时,用
ConcurrentHashMap<string atomiclong></string>或ConcurrentHashMap<string statholder></string>(StatHolder内部用AtomicLong)更安全,比synchronized方法块更轻量; -
动态配置热更新的同步点:如监听配置中心变更后,刷新 Druid 的
filter列表或connectionProperties,此时可用synchronized(YourConfigManager.class)保证单次刷新原子性; -
自定义连接泄漏检测的全局计数器:若用静态变量记录未关闭连接数,建议用
AtomicInteger,而非synchronized块增减。
如果必须用 synchronized,锁对象怎么选
避免锁 this 或实例对象(易被外部误用),也避免锁 DruidDataSource.class(影响整个应用所有 Druid 实例)。推荐方式:
- 锁一个私有 final 对象:
private final Object configLock = new Object();—— 明确作用域,不干扰其他逻辑; - 锁当前 DataSource 实例的某个稳定字段(如
druidDataSource.getConnectProperties()不推荐,因可能为 null 或变); - 对静态配置类,锁其 class 对象:
synchronized (MyDruidExtension.class),确保类级别单例安全。
比 synchronized 更合适的替代方案
多数二次开发场景下,以下方式比粗粒度 synchronized 更合理:
-
CAS 类原子类:如
AtomicBoolean控制初始化开关,LongAdder做高并发计数; - 显式 ReentrantLock:支持超时、中断、公平性,适合需精细控制的长临界区;
- ThreadLocal:对线程隔离数据(如上下文 traceId、临时缓存),完全规避同步;
-
Druid 自带扩展点:优先通过
Filter接口(如dataSourceConnectAfter)、ConnectionProxy或DruidDataSource.setConnectionInitSqls()等钩子注入逻辑,它们天然运行在连接生命周期内,无需额外加锁。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











