Ordinarily when getPooledConnection() is called it will lock the connections map (0x00000000861e8e18) and then perform a Connections.get() on an individual Connections object. Since Connections.get() is synchronised, this Connections object is locked. In parallel to this the PoolCleaner thread may call cleanup() which in turn calls Pool.expire(). This call grabs a lock to the connections map (0x00000000861e8e18) and then attempts to run Connections.expire() on an individual Connections object. Unfortunately in this instance the PoolCleaner thread is grabbing the connections map (0x00000000861e8e18) before getPooledConnection() can lock it, but *after* our thread has locked the Connections object (0x000000008b0c96d0) in the previous recursive set of calls. The situation is complicated by both the recursion and the fact that we are locking on a different Connections object than we had been previously in the stack.