美文网首页
十、HikariCP源码分析之ConcurrentBag三

十、HikariCP源码分析之ConcurrentBag三

作者: 枫山别院 | 来源:发表于2022-06-29 09:12 被阅读0次

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

    源代码版本2.4.5-SNAPSHOT

    ⑧还回连接

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

    请看代码:

    /**
     * 该方法将借出去的连接还回到连接池中
     * 不通过该方法还回的连接会造成内存泄露
     *
     * @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。

    ⑩添加连接

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

    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。

    ⑪移除连接

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

    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中重要的方法我们就分析完了,欢迎大家一起讨论。

    相关文章

      网友评论

          本文标题:十、HikariCP源码分析之ConcurrentBag三

          本文链接:https://www.haomeiwen.com/subject/fikivrtx.html