在开发数据库JDBC程序程序时,如果出现死锁:
举例 :
A线程开启事务
B线程开启事务
A 线程更新表 update table set name=? where id=1//--成功
B 线程更新表 update table set name=? where id=2//--成功
----------------A与B操作成功。
A线程更新表 update table set name=? where id=2//这时由于id=2记录被B线程connection锁定,因此等待B 线程完成事 务,释放锁。
B线程更新表 update table set name=? where id=1//这时由于id=1记录被A线程connection锁定,因此等待A 线程完成事务,释放锁。
即A线程与B线程都被阻塞,相互等待对象方释放锁,才能继续执行。
因此 A线程与B线程的 数据库操作产生死锁。
死锁会导至两条线程都一直阻塞,connection连接被占用不释放,线程被占用不释放,id=1,id=2的两条记录被锁定,其它线程更新也会阻塞。
PostgreSQL 9.5.2 版本、JDBC版本:9.4:
能够检测到死锁,并使其中一个connection的sql执行抛出异常,另一个connection操作成功。
jdbc 异常信息如下:
Caused by: org.postgresql.util.PSQLException: ERROR: deadlock detected 详细:Process 21853 waits for ShareLock on transaction 74456083; blocked by process 21852. Process 21852 waits for ShareLock on transaction 74456084; blocked by process 21853. 建议:See server log for query details. 在位置:while updating tuple (0,9) in relation "test_user" at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2270) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1998) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255) at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:570) at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:420) at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:413) at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:493) at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:45) at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:73) at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:49) at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:115) at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75) at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:170) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:386)
PostgreSQL 能够检测死锁的前提是 两个connection 相互等待对象释放锁。
对于另一种情况:
A启用事务
B启用事务
A connction 更新 update table set name =? where id=1
并且因为某种原因一直未提交事务,使id=1的记录一直被锁定
B connection 更新update table set name =? where id=1
由于该记录被Aconnction锁定,B 一直等待Aconnection commit 或 rollback 来释放锁。
这样B 操作一直阻塞中。
这种情况 一个connection 一直等待另一个connection 释放锁,这种不是死锁,但会一直阻塞。
解决办法 可以设置java.sql.Statement.setQueryTimeout(),来设置sql操作的超时时间,如果超时
则抛出异常,防止阻塞。 (前提是需要jdbc驱动支持)