前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >十、HikariCP源码分析之ConcurrentBag三

十、HikariCP源码分析之ConcurrentBag三

原创
作者头像
用户1422411
发布2022-06-25 17:55:57
3940
发布2022-06-25 17:55:57
举报

欢迎访问我的博客,同步更新: 枫山别院

源代码版本2.4.5-SNAPSHOT

⑧还回连接

这节我们要分析一下将数据库连接还回到连接池的方法requite

请看代码:

代码语言:java
复制
/**
 * 该方法将借出去的连接还回到连接池中
 * 不通过该方法还回的连接会造成内存泄露
 *
 * @param bagEntry the value to return to the bag
 * @throws NullPointerException  if value is null
 * @throws IllegalStateException if the requited value was not borrowed from the bag
 */
public void requite(final T bagEntry) {
   //⑧
   //lazySet方法不能保证连接会立刻被设置成未使用状态, 这是个延迟方法
   //这是一种优化, 如果要立即生效的话, 可能会需要使用volatile等, 让其他线程立即发现, 这会降低性能, 使用lazySet浪费不了多少时间, 但是不会浪费性能
   bagEntry.lazySet(STATE_NOT_IN_USE);

   //⑨
   //将连接放回到threadLocal中
   final List<Object> threadLocalList = threadList.get();
   if (threadLocalList != null) {
      threadLocalList.add(weakThreadLocals ? new WeakReference<>(bagEntry) : bagEntry);
   }
   //通知等待线程, 有可用连接
   synchronizer.signal();
}

一般我们都是通过 Spring 来使用 HikariCP 的,自己动手启动一个连接池的机会还是少。在 Spring 中使用非常方便,一切都是 Spring 帮我们搞定,我们只管使用,所以需要将连接还回连接池的机会也比较少,也有可能你是间接用过,比如从 HikariCP 中借用的连接,用完之后调用了 close方法,连接其实并没有真正的被关闭,而是还回了连接池,真正的close方法被 HikariCP 重写了。其实这是一个至关重要的方法,如果借用出去的连接,不通过这个方法还回来,会导致内存泄露的。

我们来分析下这个方法。

bagEntry.lazySet(STATE_NOT_IN_USE);这个很眼熟,我们在前面见过compareAndSet方法,从字面意思理解,这是一个延迟修改状态的方法,lazySet方法不能保证连接会立刻被设置成未使用状态, 这是个延迟方法,因为这是将连接还回去,时效要求并不是那么高,延迟个几十几百毫秒,对用户没有任何影响。反之,如果要立即让状态生效,让其他线程立即能发现的话,那么可能要使用volatile等,这可能会得不偿失。

⑨放到线程本地threadList

我们前面说过,还回去的连接也会放到线程本地的ThreadLocal中,方便该线程再次请求连接的时候,可以节省时间,提高性能。

这里的synchronizer.signal();方法,是通知其他线程有可用的连接加入到连接池了。这里的通知并不是线程间通信的那个通知,只是计数器加 1 了而已,我们在上一节里提过,在循环遍历完连接池没有拿到连接之后,是会检查这个synchronizer的值,如果比循环之前变大了,就是有可用连接加入到连接池了,这里是其中一个修改synchronizer的地方,还有创建连接之后,也会将synchronizer加 1。

⑩添加连接

向连接池中添加一个连接。

代码语言:java
复制
public void add(final T bagEntry) {
   if (closed) {
      LOGGER.info("ConcurrentBag has been closed, ignoring add()");
      throw new IllegalStateException("ConcurrentBag has been closed, ignoring add()");
   }
   //⑩
   sharedList.add(bagEntry);
   synchronizer.signal();
}

这里的bagEntry是一个连接的包装对象,添加一个的话,就是加入到sharedList中,而synchronizer.signal();的作用我们上面刚刚分析过了,就是可用连接的计数器加 1。

⑪移除连接

该方法是从连接池中移除一个连接,是真正的删除。

代码语言:java
复制
public boolean remove(final T bagEntry) {
   //⑪
   //尝试标记移除使用中和保留状态的连接, 如果标记失败, 就是空闲的连接, 直接返回 false
   //也就是检查连接的状态, 不能移除空闲的连接或者已经标记移除的连接
   if (!bagEntry.compareAndSet(STATE_IN_USE, STATE_REMOVED) && !bagEntry.compareAndSet(STATE_RESERVED, STATE_REMOVED) && !closed) {
      LOGGER.warn("Attempt to remove an object from the bag that was not borrowed or reserved: {}", bagEntry);
      return false;
   }
   //如果上面标记成功了, 那么从连接池中移除这个连接
   final boolean removed = sharedList.remove(bagEntry);
   if (!removed && !closed) {
      LOGGER.warn("Attempt to remove an object from the bag that does not exist: {}", bagEntry);
   }

   // synchronizer.signal();
   return removed;
}

这个remove方法,并不是能移除所有的连接,它只能移除两种状态的连接,分别是STATE_IN_USESTATE_RESERVED。我们看⑪处的代码,如果不是这两个状态,那么会直接打印警告,移除不了。连接有四个状态,除了这两个,还有就是已删除状态,自然不能再次删除了;还有一个就是未使用状态了,也就是说,我们要移除一个未使用状态的连接,那是不行的。

后面就简单了,如果状态修改成功了,那么就从连接池中删除这个连接就可以了,收工!

至此,ConcurrentBag中重要的方法我们就分析完了,欢迎大家一起讨论。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ⑧还回连接
  • ⑨放到线程本地threadList
  • ⑩添加连接
  • ⑪移除连接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档