一则死锁分析小记

听说发生死锁,我说赶紧jstack搞个javacore分析下。

用IBM的jca打开,看线程,很多是在等待获取连接。

"ConsumeMessageThread_15" daemon prio=10 tid=0x00007fdbc04a4000 nid=0x552d in Object.wait() [0x00007fdbe1bdd000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007847a8ac0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch)
    at java.lang.Object.wait(Object.java:503)
    at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1118)
    - locked <0x00000007847a8ac0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch)
    at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
    at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:83)
    at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:69)
    at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:279)
    at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:72)
    at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:47)
    at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:105)
    at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:71)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:152)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:141)


如果是等待获取连接,为什么获取不到呢。

查看monitor details。ConsumeMessageThread_9拥有锁0x00000007834414c0,而有其他七个线程等待这个锁。

"ConsumeMessageThread_9" daemon prio=10 tid=0x00007fdbdc00d000 nid=0x5523 in Object.wait() [0x00007fdbe25e7000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007847a8af0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch)
    at java.lang.Object.wait(Object.java:503)
    at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:1118)
    - locked <0x00000007847a8af0> (a org.apache.commons.pool.impl.GenericObjectPool$Latch)
    at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:106)
    at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:280)
    at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:320)
    at org.springframework.jdbc.support.SQLErrorCodesFactory.getErrorCodes(SQLErrorCodesFactory.java:214)
    - locked <0x00000007846b7450> (a java.util.WeakHashMap)
  
  at 
org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.setDataSource(SQLErrorCodeSQLExceptionTranslator.java:140)
  
  at 
org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.<init>(SQLErrorCodeSQLExceptionTranslator.java:103)
    at org.mybatis.spring.MyBatisExceptionTranslator.initExceptionTranslator(MyBatisExceptionTranslator.java:85)
    - locked <0x00000007834414c0> (a org.mybatis.spring.MyBatisExceptionTranslator)
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:72)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:368)
    at com.sun.proxy.$Proxy12.insert(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:240)


某个正在等待锁的堆栈。

"ConsumeMessageThread_16" daemon prio=10 tid=0x00007fdc00005800 nid=0x552c waiting for monitor entry [0x00007fdbe1cdf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at org.mybatis.spring.MyBatisExceptionTranslator.initExceptionTranslator(MyBatisExceptionTranslator.java:84)
    - waiting to lock <0x00000007834414c0> (a org.mybatis.spring.MyBatisExceptionTranslator)
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:72)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:368)
    at com.sun.proxy.$Proxy12.insert(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.insert(SqlSessionTemplate.java:240)


通过上面的堆栈,很明显,是insert失败的时候,myBaits要将数据库的异常做一次转换,转换成统一的错误码。转换的时候,要获取数据库连接,通过数据库的元信息,获取当前连接的是什么类型的数据库。

问题来了,获取连接时,业务出错的连接应该没有释放掉。如果当前连接数已经到达上限,此处就需要等待获取新的连接。

但是,有两个问题。

  1. 一般等待获取连接,都会设置最低超时时间。如果有超时,这个地方就在到达超时,解除互锁。

  2. 从分析得没错,8个数据库连接就达到连接池的上限了。一般,不至于设置这么少的吧,怎么我们也是有千万用户的啊。


看看连接池的配置:

    <bean id="XXX class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName">
            <value>com.mysql.jdbc.Driver</value>
        </property>
        <property name="username">
            <value>${mysql.w.username}</value>
        </property>
        <property name="password">
            <value>${mysql.w.password}</value>
        </property>
        <property name="url">
            <value>${mysql.w.url}</value>
        </property>
        <property name="validationQuery">
            <value>SELECT 1</value>
        </property>
        <property name="testOnBorrow">
            <value>true</value>
        </property>
    </bean>


没有设置连接次的,最大最小值和等待获取连接的最长时间。。。那就是使用默认值了。。。

org.apache.commons.pool.impl.GenericObjectPool<T>
    /**
     * The default cap on the total number of active instances from the pool.
     * @see #getMaxActive
     */
    public static final int DEFAULT_MAX_ACTIVE  = 8;

    /**
     * The default maximum amount of time (in milliseconds) the
     * {@link #borrowObject} method should block before throwing
     * an exception when the pool is exhausted and the
     * {@link #getWhenExhaustedAction "when exhausted" action} is
     * {@link #WHEN_EXHAUSTED_BLOCK}.
     * @see #getMaxWait
     * @see #setMaxWait
     */
    public static final long DEFAULT_MAX_WAIT = -1L;


哦。。原来默认值,最大就是8,并且无限等待。。。

不合理啊。这个配置不合理。。。


这个死锁问题,把不合理的无限等待改掉,就自然解开了。

你可能感兴趣的:(一则死锁分析小记)