java线程池-使用哪种数据结构存储线程?

104 阅读6分钟

少废话,直接看源码

java线程池,使用哪种数据结构来存储线程对象呢?

java.util.concurrent.ThreadPoolExecutor#workers

/**
 * 线程池的数据结构是Set
 * 元素是工作线程:Worker
 * 工作线程的作用:执行业务任务(业务线程任务)
 * ---
 * Set containing all worker threads in pool. Accessed only when
 * holding mainLock.
 * ---
 * 为什么使用Set?
 * 因为线程不能重复。
 */
private final HashSet<Worker> workers = new HashSet<Worker>(); //线程池:Worker就相当于是线程池里的线程

可以看到,是使用set。

为什么用set?因为线程对象不能重复。

tomcat和dubbo呢?

都是基于java线程池,所以都是set。

数据库连接池呢?

比如阿里druid,是用哪个数据结构来存储数据库连接对象的呢?

少废话,也直接看源码

private volatile DruidConnectionHolder[] connections; //连接池:基于数组实现

DruidConnectionHolder类,持有连接对象

/**
 * 持有连接的类
 *
 * @author wenshao [szujobs@hotmail.com]
 */
public final class DruidConnectionHolder {

protected final long connectionId; //连接id:每个连接必须唯一,否则乱套了
protected final Connection conn; //数据库连接对象:就是sql包里的连接类

所以,我们可以看到,java线程池是基于set,而druid是基于数组,本质都是集合(对象池),然后集合里的元素是对象。

set,是自动去重。

数组,需要自己去重。所以,DruidConnectionHolder类,除了包含连接对象字段,还包含了连接id字段。

何时创建连接池?

入口:com.alibaba.druid.pool.DruidDataSource#getConnection(long)

/**
 * 获取连接
 * 
 * @param maxWaitMillis 最大等待时间
 * @return com.alibaba.druid.pool.DruidPooledConnection
 * @author javaself
 */
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
    //初始化连接池
    init();

    //获取连接
    if (filters.size() > 0) {
        FilterChainImpl filterChain = new FilterChainImpl(this);
        return filterChain.dataSource_connect(this, maxWaitMillis);
    } else {
        return getConnectionDirect(maxWaitMillis); //获取连接
    }
}

初始化的时候,会创建连接池:具体来说,是如果没有创建连接池,那么就创建;如果已经创建,那么直接返回。

/**
 * 初始化连接池
 *
 * @author javaself
 */
public void init() throws SQLException {
    //判断是否已经初始化
    if (inited) { //如果已经初始化,直接返回
        return;
    }

    //如果没有初始化,就继续初始化
    // bug fixed for dead lock, for issue #2980
    DruidDriver.getInstance();

    final ReentrantLock lock = this.lock;
    try {
        lock.lockInterruptibly();
    } catch (InterruptedException e) {
        throw new SQLException("interrupt", e);
    }

    boolean init = false;
    try {
        if (inited) {
            return;
        }

        initStackTrace = Utils.toString(Thread.currentThread().getStackTrace());

        this.id = DruidDriver.createDataSourceId();
        if (this.id > 1) {
            long delta = (this.id - 1) * 100000;
            this.connectionIdSeedUpdater.addAndGet(this, delta);
            this.statementIdSeedUpdater.addAndGet(this, delta);
            this.resultSetIdSeedUpdater.addAndGet(this, delta);
            this.transactionIdSeedUpdater.addAndGet(this, delta);
        }

        if (this.jdbcUrl != null) {
            this.jdbcUrl = this.jdbcUrl.trim();
            initFromWrapDriverUrl();
        }

        for (Filter filter : filters) {
            filter.init(this);
        }

        if (this.dbTypeName == null || this.dbTypeName.length() == 0) {
            this.dbTypeName = JdbcUtils.getDbType(jdbcUrl, null);
        }

        DbType dbType = DbType.of(this.dbTypeName);
        if (JdbcUtils.isMysqlDbType(dbType)) {
            boolean cacheServerConfigurationSet = false;
            if (this.connectProperties.containsKey("cacheServerConfiguration")) {
                cacheServerConfigurationSet = true;
            } else if (this.jdbcUrl.indexOf("cacheServerConfiguration") != -1) {
                cacheServerConfigurationSet = true;
            }
            if (cacheServerConfigurationSet) {
                this.connectProperties.put("cacheServerConfiguration", "true");
            }
        }

        if (maxActive <= 0) {
            throw new IllegalArgumentException("illegal maxActive " + maxActive);
        }

        if (maxActive < minIdle) {
            throw new IllegalArgumentException("illegal maxActive " + maxActive);
        }

        if (getInitialSize() > maxActive) {
            throw new IllegalArgumentException("illegal initialSize " + this.initialSize + ", maxActive " + maxActive);
        }

        if (timeBetweenLogStatsMillis > 0 && useGlobalDataSourceStat) {
            throw new IllegalArgumentException("timeBetweenLogStatsMillis not support useGlobalDataSourceStat=true");
        }

        if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) {
            throw new SQLException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");
        }

        if (keepAlive && keepAliveBetweenTimeMillis <= timeBetweenEvictionRunsMillis) {
            throw new SQLException("keepAliveBetweenTimeMillis must be grater than timeBetweenEvictionRunsMillis");
        }

        if (this.driverClass != null) {
            this.driverClass = driverClass.trim();
        }

        initFromSPIServiceLoader();

        resolveDriver();

        initCheck();

        this.netTimeoutExecutor = new SynchronousExecutor();

        initExceptionSorter();
        initValidConnectionChecker();
        validationQueryCheck();

        if (isUseGlobalDataSourceStat()) {
            dataSourceStat = JdbcDataSourceStat.getGlobal();
            if (dataSourceStat == null) {
                dataSourceStat = new JdbcDataSourceStat("Global", "Global", this.dbTypeName);
                JdbcDataSourceStat.setGlobal(dataSourceStat);
            }
            if (dataSourceStat.getDbType() == null) {
                dataSourceStat.setDbType(this.dbTypeName);
            }
        } else {
            dataSourceStat = new JdbcDataSourceStat(this.name, this.jdbcUrl, this.dbTypeName, this.connectProperties);
        }
        dataSourceStat.setResetStatEnable(this.resetStatEnable);

        //创建连接池
        connections = new DruidConnectionHolder[maxActive];
        evictConnections = new DruidConnectionHolder[maxActive];
        keepAliveConnections = new DruidConnectionHolder[maxActive];

        SQLException connectError = null;

        if (createScheduler != null && asyncInit) {
            for (int i = 0; i < initialSize; ++i) {
                submitCreateTask(true);
            }
        } else if (!asyncInit) {
            // init connections
            while (poolingCount < initialSize) {
                try {
                    PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
                    DruidConnectionHolder holder = new DruidConnectionHolder(this, pyConnectInfo);
                    connections[poolingCount++] = holder;
                } catch (SQLException ex) {
                    LOG.error("init datasource error, url: " + this.getUrl(), ex);
                    if (initExceptionThrow) {
                        connectError = ex;
                        break;
                    } else {
                        Thread.sleep(3000);
                    }
                }
            }

            if (poolingCount > 0) {
                poolingPeak = poolingCount;
                poolingPeakTime = System.currentTimeMillis();
            }
        }

        createAndLogThread();
        createAndStartCreatorThread();
        createAndStartDestroyThread();

        initedLatch.await();
        init = true;

        initedTime = new Date();
        registerMbean();

        if (connectError != null && poolingCount == 0) {
            throw connectError;
        }

        if (keepAlive) {
            // async fill to minIdle
            if (createScheduler != null) {
                for (int i = 0; i < minIdle; ++i) {
                    submitCreateTask(true);
                }
            } else {
                this.emptySignal();
            }
        }

    } catch (SQLException e) {
        LOG.error("{dataSource-" + this.getID() + "} init error", e);
        throw e;
    } catch (InterruptedException e) {
        throw new SQLException(e.getMessage(), e);
    } catch (RuntimeException e) {
        LOG.error("{dataSource-" + this.getID() + "} init error", e);
        throw e;
    } catch (Error e) {
        LOG.error("{dataSource-" + this.getID() + "} init error", e);
        throw e;

    } finally {
        inited = true;
        lock.unlock();

        if (init && LOG.isInfoEnabled()) {
            String msg = "{dataSource-" + this.getID();

            if (this.name != null && !this.name.isEmpty()) {
                msg += ",";
                msg += this.name;
            }

            msg += "} inited";

            LOG.info(msg);
        }
    }
}

核心代码

//创建连接池
connections = new DruidConnectionHolder[maxActive];

其实就是new一个数组而已,然后数组元素是连接对象。

如何获取连接?

获取连接的入口,也是刚才那个入口。如果已经初始化,那么就直接获取连接:

/**
 * 获取连接
 *
 * @param maxWaitMillis
 * @return com.alibaba.druid.pool.DruidPooledConnection
 * @author javaself
 */
public DruidPooledConnection getConnectionDirect(long maxWaitMillis) throws SQLException {
    int notFullTimeoutRetryCnt = 0;
    for (; ; ) {
        // handle notFullTimeoutRetry
        DruidPooledConnection poolableConnection;
        try {
            //获取连接
            poolableConnection = getConnectionInternal(maxWaitMillis);
        } catch (GetConnectionTimeoutException ex) {
            if (notFullTimeoutRetryCnt <= this.notFullTimeoutRetryCount && !isFull()) {
                notFullTimeoutRetryCnt++;
                if (LOG.isWarnEnabled()) {
                    LOG.warn("get connection timeout retry : " + notFullTimeoutRetryCnt);
                }
                continue;
            }
            throw ex;
        }

        if (testOnBorrow) {
            boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
            if (!validate) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("skip not validate connection.");
                }

                discardConnection(poolableConnection.holder);
                continue;
            }
        } else {
            if (poolableConnection.conn.isClosed()) {
                discardConnection(poolableConnection.holder); // 传入null,避免重复关闭
                continue;
            }

            if (testWhileIdle) {
                final DruidConnectionHolder holder = poolableConnection.holder;
                long currentTimeMillis = System.currentTimeMillis();
                long lastActiveTimeMillis = holder.lastActiveTimeMillis;
                long lastExecTimeMillis = holder.lastExecTimeMillis;
                long lastKeepTimeMillis = holder.lastKeepTimeMillis;

                if (checkExecuteTime
                        && lastExecTimeMillis != lastActiveTimeMillis) {
                    lastActiveTimeMillis = lastExecTimeMillis;
                }

                if (lastKeepTimeMillis > lastActiveTimeMillis) {
                    lastActiveTimeMillis = lastKeepTimeMillis;
                }

                long idleMillis = currentTimeMillis - lastActiveTimeMillis;

                long timeBetweenEvictionRunsMillis = this.timeBetweenEvictionRunsMillis;

                if (timeBetweenEvictionRunsMillis <= 0) {
                    timeBetweenEvictionRunsMillis = DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS;
                }

                if (idleMillis >= timeBetweenEvictionRunsMillis
                        || idleMillis < 0 // unexcepted branch
                ) {
                    boolean validate = testConnectionInternal(poolableConnection.holder, poolableConnection.conn);
                    if (!validate) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("skip not validate connection.");
                        }

                        discardConnection(poolableConnection.holder);
                        continue;
                    }
                }
            }
        }

        if (removeAbandoned) {
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            poolableConnection.connectStackTrace = stackTrace;
            poolableConnection.setConnectedTimeNano();
            poolableConnection.traceEnable = true;

            activeConnectionLock.lock();
            try {
                activeConnections.put(poolableConnection, PRESENT);
            } finally {
                activeConnectionLock.unlock();
            }
        }

        if (!this.defaultAutoCommit) {
            poolableConnection.setAutoCommit(false);
        }

        return poolableConnection;
    }
}

继续调用下面的方法,获取连接

/**
 * 获取连接
 *
 * @param maxWait
 * @return com.alibaba.druid.pool.DruidPooledConnection
 * @author javaself
 */
private DruidPooledConnection getConnectionInternal(long maxWait) throws SQLException {
    if (closed) {
        connectErrorCountUpdater.incrementAndGet(this);
        throw new DataSourceClosedException("dataSource already closed at " + new Date(closeTimeMillis));
    }

    if (!enable) {
        connectErrorCountUpdater.incrementAndGet(this);

        if (disableException != null) {
            throw disableException;
        }

        throw new DataSourceDisableException();
    }

    final long nanos = TimeUnit.MILLISECONDS.toNanos(maxWait);
    final int maxWaitThreadCount = this.maxWaitThreadCount;

    DruidConnectionHolder holder;

    for (boolean createDirect = false; ; ) {
        if (createDirect) {
            createStartNanosUpdater.set(this, System.nanoTime());
            if (creatingCountUpdater.compareAndSet(this, 0, 1)) {
                PhysicalConnectionInfo pyConnInfo = DruidDataSource.this.createPhysicalConnection();
                holder = new DruidConnectionHolder(this, pyConnInfo);
                holder.lastActiveTimeMillis = System.currentTimeMillis();

                creatingCountUpdater.decrementAndGet(this);
                directCreateCountUpdater.incrementAndGet(this);

                if (LOG.isDebugEnabled()) {
                    LOG.debug("conn-direct_create ");
                }

                boolean discard;
                lock.lock();
                try {
                    if (activeCount < maxActive) {
                        activeCount++;
                        holder.active = true;
                        if (activeCount > activePeak) {
                            activePeak = activeCount;
                            activePeakTime = System.currentTimeMillis();
                        }
                        break;
                    } else {
                        discard = true;
                    }
                } finally {
                    lock.unlock();
                }

                if (discard) {
                    JdbcUtils.close(pyConnInfo.getPhysicalConnection());
                }
            }
        }

        try {
            lock.lockInterruptibly();
        } catch (InterruptedException e) {
            connectErrorCountUpdater.incrementAndGet(this);
            throw new SQLException("interrupt", e);
        }

        try {
            if (maxWaitThreadCount > 0
                    && notEmptyWaitThreadCount >= maxWaitThreadCount) {
                connectErrorCountUpdater.incrementAndGet(this);
                throw new SQLException("maxWaitThreadCount " + maxWaitThreadCount + ", current wait Thread count "
                        + lock.getQueueLength());
            }

            if (onFatalError
                    && onFatalErrorMaxActive > 0
                    && activeCount >= onFatalErrorMaxActive) {
                connectErrorCountUpdater.incrementAndGet(this);

                StringBuilder errorMsg = new StringBuilder();
                errorMsg.append("onFatalError, activeCount ")
                        .append(activeCount)
                        .append(", onFatalErrorMaxActive ")
                        .append(onFatalErrorMaxActive);

                if (lastFatalErrorTimeMillis > 0) {
                    errorMsg.append(", time '")
                            .append(StringUtils.formatDateTime19(
                                    lastFatalErrorTimeMillis, TimeZone.getDefault()))
                            .append("'");
                }

                if (lastFatalErrorSql != null) {
                    errorMsg.append(", sql \n")
                            .append(lastFatalErrorSql);
                }

                throw new SQLException(
                        errorMsg.toString(), lastFatalError);
            }

            connectCount++;

            if (createScheduler != null
                    && poolingCount == 0
                    && activeCount < maxActive
                    && creatingCountUpdater.get(this) == 0
                    && createScheduler instanceof ScheduledThreadPoolExecutor) {
                ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) createScheduler;
                if (executor.getQueue().size() > 0) {
                    createDirect = true;
                    continue;
                }
            }

            //获取连接
            if (maxWait > 0) {
                holder = pollLast(nanos);
            } else {
                holder = takeLast();
            }

            if (holder != null) {
                if (holder.discard) {
                    continue;
                }

                activeCount++;
                holder.active = true;
                if (activeCount > activePeak) {
                    activePeak = activeCount;
                    activePeakTime = System.currentTimeMillis();
                }
            }
        } catch (InterruptedException e) {
            connectErrorCountUpdater.incrementAndGet(this);
            throw new SQLException(e.getMessage(), e);
        } catch (SQLException e) {
            connectErrorCountUpdater.incrementAndGet(this);
            throw e;
        } finally {
            lock.unlock();
        }

        break;
    }

    //
    if (holder == null) {
        long waitNanos = waitNanosLocal.get();

        final long activeCount;
        final long maxActive;
        final long creatingCount;
        final long createStartNanos;
        final long createErrorCount;
        final Throwable createError;
        try {
            lock.lock();
            activeCount = this.activeCount;
            maxActive = this.maxActive;
            creatingCount = this.creatingCount;
            createStartNanos = this.createStartNanos;
            createErrorCount = this.createErrorCount;
            createError = this.createError;
        } finally {
            lock.unlock();
        }

        StringBuilder buf = new StringBuilder(128);
        buf.append("wait millis ")
                .append(waitNanos / (1000 * 1000))
                .append(", active ").append(activeCount)
                .append(", maxActive ").append(maxActive)
                .append(", creating ").append(creatingCount);

        if (creatingCount > 0 && createStartNanos > 0) {
            long createElapseMillis = (System.nanoTime() - createStartNanos) / (1000 * 1000);
            if (createElapseMillis > 0) {
                buf.append(", createElapseMillis ").append(createElapseMillis);
            }
        }

        if (createErrorCount > 0) {
            buf.append(", createErrorCount ").append(createErrorCount);
        }

        List<JdbcSqlStatValue> sqlList = this.getDataSourceStat().getRuningSqlList();
        for (int i = 0; i < sqlList.size(); ++i) {
            if (i != 0) {
                buf.append('\n');
            } else {
                buf.append(", ");
            }
            JdbcSqlStatValue sql = sqlList.get(i);
            buf.append("runningSqlCount ").append(sql.getRunningCount());
            buf.append(" : ");
            buf.append(sql.getSql());
        }

        String errorMessage = buf.toString();

        if (createError != null) {
            throw new GetConnectionTimeoutException(errorMessage, createError);
        } else {
            throw new GetConnectionTimeoutException(errorMessage);
        }
    }

    holder.incrementUseCount();

    DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
    return poolalbeConnection;
}

核心代码

//获取连接
if (maxWait > 0) {
    holder = pollLast(nanos); //获取连接
} else {
    holder = takeLast();
}

继续看pollLast方法

/**
 * 获取连接
 *
 * @param nanos
 * @return com.alibaba.druid.pool.DruidConnectionHolder
 * @author javaself
 */
private DruidConnectionHolder pollLast(long nanos) throws InterruptedException, SQLException {
    long estimate = nanos;

    for (; ; ) {
        if (poolingCount == 0) {
            emptySignal(); // send signal to CreateThread create connection

            if (failFast && isFailContinuous()) {
                throw new DataSourceNotAvailableException(createError);
            }

            if (estimate <= 0) {
                waitNanosLocal.set(nanos - estimate);
                return null;
            }

            notEmptyWaitThreadCount++;
            if (notEmptyWaitThreadCount > notEmptyWaitThreadPeak) {
                notEmptyWaitThreadPeak = notEmptyWaitThreadCount;
            }

            try {
                long startEstimate = estimate;
                estimate = notEmpty.awaitNanos(estimate); // signal by
                // recycle or
                // creator
                notEmptyWaitCount++;
                notEmptyWaitNanos += (startEstimate - estimate);

                if (!enable) {
                    connectErrorCountUpdater.incrementAndGet(this);

                    if (disableException != null) {
                        throw disableException;
                    }

                    throw new DataSourceDisableException();
                }
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to non-interrupted thread
                notEmptySignalCount++;
                throw ie;
            } finally {
                notEmptyWaitThreadCount--;
            }

            if (poolingCount == 0) {
                if (estimate > 0) {
                    continue;
                }

                waitNanosLocal.set(nanos - estimate);
                return null;
            }
        }

        decrementPoolingCount();
        //获取连接
        DruidConnectionHolder last = connections[poolingCount]; //其实就是从数组中获取连接:从数组的最后一个元素开始获取
        connections[poolingCount] = null;

        long waitNanos = nanos - estimate;
        last.setLastNotEmptyWaitNanos(waitNanos);

        return last;
    }
}

核心代码

//获取连接
DruidConnectionHolder last = connections[poolingCount]; //其实就是从数组中获取连接:从数组的最后一个元素获取
connections[poolingCount] = null; //将数组中的最后一个元素置为null,表示该元素已经被获取了

其实就是从数组中获取连接:从数组的最后一个元素获取