1. 程式人生 > >PageHelper Reentrantlock鎖使用問題

PageHelper Reentrantlock鎖使用問題

Mybatis-PageHelper應該是目前使用比較廣泛的一個Mybatis分頁外掛。我在幾個專案裡都引入了該外掛。今天偶然閱讀其原始碼,卻發現了一個不小的問題。

注:我閱讀的是最新的4.1.3的原始碼。

該分頁外掛的核心是PageHelper類,該類實現了Mybatis提供的Interceptor介面。介面定義的intercept方法實現如下:

/**
 * Mybatis攔截器方法
 *
 * @param invocation 攔截器入參
 * @return 返回執行結果
 * @throws Throwable 丟擲異常
 */
public Object intercept
(Invocation invocation) throws Throwable { if (autoRuntimeDialect) { SqlUtil sqlUtil = getSqlUtil(invocation); return sqlUtil.processPage(invocation); } else { if (autoDialect) { initSqlUtil(invocation); } return sqlUtil.processPage(invocation
); } }

其中,getSqlUtil方法,實現如下:

/**
 * 根據daatsource建立對應的sqlUtil
 *
 * @param invocation
 */
public SqlUtil getSqlUtil(Invocation invocation) {
    MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
    //改為對dataSource做快取
    DataSource dataSource = ms.getConfiguration().getEnvironment().
getDataSource(); String url = getUrl(dataSource); if (urlSqlUtilMap.containsKey(url)) { return urlSqlUtilMap.get(url); } ReentrantLock lock = new ReentrantLock(); //這裡我認為是有問題的。 try { lock.lock(); if (urlSqlUtilMap.containsKey(url)) { return urlSqlUtilMap.get(url); } if (StringUtil.isEmpty(url)) { throw new RuntimeException("無法自動獲取jdbcUrl,請在分頁外掛中配置dialect引數!"); } String dialect = Dialect.fromJdbcUrl(url); if (dialect == null) { throw new RuntimeException("無法自動獲取資料庫型別,請通過dialect引數指定!"); } SqlUtil sqlUtil = new SqlUtil(dialect); if (this.properties != null) { sqlUtil.setProperties(properties); } else if (this.sqlUtilConfig != null) { sqlUtil.setSqlUtilConfig(this.sqlUtilConfig); } urlSqlUtilMap.put(url, sqlUtil); return sqlUtil; } finally { lock.unlock(); } }

這裡有個加鎖的操作,使用的是Reentrantlock區域性變數。這裡是有問題的,區域性變數的lock是無法起到鎖的作用的。這段程式碼實際上仍不是執行緒安全的。將該lock變數提升為類的全域性變數即可解決。

private ReentrantLock lock = new ReentrantLock();

/**
 * 根據datasource建立對應的sqlUtil
 *
 * @param invocation
 */
public SqlUtil getSqlUtil(Invocation invocation) {
    MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
    //改為對dataSource做快取
    DataSource dataSource = ms.getConfiguration().getEnvironment().getDataSource();
    String url = getUrl(dataSource);
    if (urlSqlUtilMap.containsKey(url)) {
        return urlSqlUtilMap.get(url);
    }
    try {
        lock.lock();
       ...

順便也修改了一個datasource拼寫錯誤的問題。

給作者提交了Pull Request。