问题说明
Spring dubbo 服务, 偶尔会报如下异常
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: interrupt
...
近期有修改过接口最外层 try-catch, 在 catch 块里添加了 Thread.currentThread.interrupt();
这是一个不当的修改
导致当有请求发生异常时, 线程会被设置中断标记, 然后线程被归还回 Dubbo 线程池, 当之后的某个请求使用了该线程, 执行到 DruidDataSource.getConnectInternal()
方法时, 会加一个可以响应中断的锁, 当有中断异常时, 会抛一个 SQLException(“interrupt”, e), 该方法内部调用到了 AQS.acquireInterruptibly
, 里面会先判断当前线程是否被设置了中断标记, 如果有则直接抛出中断异常, 所以最终就抛出了 SQLException, 且 message 为 interrupt
# DruidDataSource.getConnectInternal()
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
connectErrorCount.incrementAndGet();
throw new SQLException("interrupt", e);
}
# java.util.concurrent.locks.ReentrantLock#lockInterruptibly
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
# java.util.concurrent.locks.AbstractQueuedSynchronizer#acquireInterruptibly
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
问题解决
删除不当位置的 Thread.currentThread.interrupt();
建议, 除非你真的知道为什么需要 Thread.currentThread.interrupt();
, 否则不要瞎用
要用也是在 InterruptedException 的 catch 中用, 不要在 Throwable / Exception 级别的 catch 中用