// 回收长期未归还的连接(再次说明:该方法仅在 removeAbandoned 设置为 true 的情况下触发)
public int removeAbandoned() {
    int removeCount = 0;

    long currrentNanos = System.nanoTime();

    // 这个列表用于存放满足条件的真正需要强制回收的连接
    List<DruidPooledConnection> abandonedList = new ArrayList<DruidPooledConnection>();

    activeConnectionLock.lock();
    try {
        //在 removeAbandoned 设置为 true 的情况下,所有被借出去的连接,都会被保存进 activeConnections(参考主流程1),所以要进行“长期未归还”的检查,就是从 activeConnections 开始的
        Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator();

        for (; iter.hasNext();) {
            DruidPooledConnection pooledConnection = iter.next();

            if (pooledConnection.isRunning()) {
                // 如果当前连接正在使用中(指的是正在 execute),则不处理
                continue;
            }

            // 利用当前时间和连接被借出去时的时间,计算出连接被借出去的时间有多久
            long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000);

            // 如果连接被借出去的时间超过 removeAbandonedTimeoutMillis 这个阈值,将会命中“主动归还”的逻辑检查
            if (timeMillis >= removeAbandonedTimeoutMillis) {
                // 先从 activeConnections 移除
                iter.remove();
                // 标记为 false,防止回收时重复 removeactiveConnections,可以参考主流程5
                pooledConnection.setTraceEnable(false);
                // 放入“强制回收”队列
                abandonedList.add(pooledConnection);
            }
        }
    } finally {
        activeConnectionLock.unlock();
    }

    // 如果“强制回收”队列大于 0,说明有需要回收的连接
    if (abandonedList.size() > 0) {
        // 循环这些连接
        for (DruidPooledConnection pooledConnection : abandonedList) {
            final ReentrantLock lock = pooledConnection.lock;
            // 拿到连接的锁
            lock.lock();
            try {
                // 已经被回收的,则不管
                if (pooledConnection.isDisable()) {
                    continue;
                }
            } finally {
                lock.unlock();
            }

            // 触发回收连接对象(pooledConnection)里的 holcder(注意这里其实是把 pooledConnection 对象里的 holder 给回收至连接池了,pooledConnection 对象本身会被销毁)
            // 这里触发的 close,是 DruidPooledConnection 的 close,也就是会触发 recycle 方法的 close
            JdbcUtils.close(pooledConnection);
            // 标记
            pooledConnection.abandond();
            removeAbandonedCount++;
            removeCount++;

            // 日志打印,忽略
            if (isLogAbandoned()) {
                StringBuilder buf = new StringBuilder();
                buf.append("abandon connection, owner thread: ");
                buf.append(pooledConnection.getOwnerThread().getName());
                buf.append(", connected at : ");
                buf.append(pooledConnection.getConnectedTimeMillis());
                buf.append(", open stackTrace\n");

                StackTraceElement[] trace = pooledConnection.getConnectStackTrace();
                for (int i = 0; i < trace.length; i++) {
                    buf.append("\tat ");
                    buf.append(trace[i].toString());
                    buf.append("\n");
                }

                buf.append("ownerThread current state is " + pooledConnection.getOwnerThread().getState()
                           + ", current stackTrace\n");
                trace = pooledConnection.getOwnerThread().getStackTrace();
                for (int i = 0; i < trace.length; i++) {
                    buf.append("\tat ");
                    buf.append(trace[i].toString());
                    buf.append("\n");
                }

                LOG.error(buf.toString());
            }
        }
    }

    // 返回本次被强制回收的连接个数
    return removeCount;
}