②检查本地保存的连接
//②
//如果ThreadLocal中有连接的话, 就遍历, 尝试获取
//从后往前反向遍历是有好处的, 因为最后一次使用的连接, 空闲的可能性比较大, 之前的连接可能会被其他线程偷窃走了
for (int i = list.size() - 1; i >= 0; i--) {
final Object entry = list.remove(i);
@SuppressWarnings("unchecked") final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
return bagEntry;
}
}
OK,我们继续。这里遍历的 list 变量,是threadList,是当前线程使用过的连接,保存在本地线程的引用。
可以看到这个遍历的顺序,是倒序遍历的。list 里的连接,肯定是最后一个是最后放进去的,也就是最近使用过的,这个连接还可以继续使用的可能极大,时间越早的连接,就越可能被其他线程借用走了,所以这就是为什么要倒序遍历,我们要先检查能使用的可能性最大的连接。说到这里,我们在平时的业务代码中,要用 if 检查一些条件,这时候我们要有意识的先检查可能性最大的条件,这有利于减少判断的次数,提高程序的性能。
在从 list 中获取连接的时候,使用的是remove方法,也就是说,无论如何,这个连接的引用我们不在本地保存了,如果它可以用,那么用完之后它又会加入到本地的threadList,如果不能使用了,那么我们就删除了这个无用的连接引用。
下面第 三句代码其实就是类型的强转,忽略之。有意思的是接下来第四句的判断,直接在 if 中就执行修改连接状态的操作。每个连接都有一个状态,它的类型是AtomicInteger,是个数字,并且是原子操作,线程安全。compareAndSet方法执行的时候,将STATE_NOT_IN_USE状态跟连接的当前状态对比,一样的时候才将它修改成STATE_IN_USE,既保证了线程安全,又保证了只有在连接是空闲状态才能使用线程,不会错误使用了其他状态的连接。此时,如果状态修改成功了,那么直接将该连接返回给用户使用。
说到这里,应该说一下连接有哪些状态。
连接的状态
STATE_NOT_IN_USE = 0 //空闲状态, 可以被借走

本文详细分析了HikariCP的ConcurrentBag在检查本地线程保存的连接及连接状态,讨论了如何确保线程安全并高效地获取连接。在遍历连接时采取倒序策略,优先检查最近使用的连接。当无法从本地获取连接时,进行一系列准备工作,包括设置超时时间,记录开始时间等。在连接池中尝试获取连接,如果成功则判断是否需要补偿其他等待线程创建新连接。如果没有获取到连接,则会根据超时时间和连接池变化情况决定是否继续尝试,直到达到超时时间,最终返回null。
最低0.47元/天 解锁文章
265

被折叠的 条评论
为什么被折叠?



