oracle锁bug

一、问题描述
两任务并发访问同一数据库表中的记录,出现死锁,如下是其中一个任务的代码:
Connection dbConn = DAMContext.getConnection();   String userAccountSql = createAccountSql(exportDate);   List accountList = new ArrayList();   try {       PreparedStatement pstm = dbConn.prepareStatement(userAccountSql,           ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);       ResultSet result = pstm.executeQuery();       while (result.next()) {           UserAccount account = new UserAccount();           //omited           accountList.add(account);       }          String updateSql = createExpireSql(exportDate);       PreparedStatement updatePs = dbConn.prepareStatement(updateSql,              ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); 
    //ResultSet.CONCUR_READ_ONLY 表示sql语句不会做更新处理,       //这就意味者在操作这批记录时,不会首先获取这批记录的"页锁",别人仍旧可以同时对这批数据进行并发的update操作。          ResultSet updateResult = updatePs.executeQuery();       //执行update操作       //在dbConn.prepareStatement()中指定的是不做update处理,而实际上却执行了update操作       //导致更新这批记录时:更新到某条记录时,才获取这条记录的"独占行锁"。       //这就留下了发生死锁的隐患:在执行过程中,一部分已更新的记录集(A)获取了锁;另一部分还没有更新的记录集(B)没有获取锁       //如果此时,有另一线程已经update了记录集(B)中的记录,并且未释放锁;同时又等待记录集(A)中记录的锁来进行update操作,这样死锁就发生了。   }   catch (Exception e) {       e.printStackTrace();       try {           dbConn.rollback();       }       catch (SQLException _ex) {           _ex.printStackTrace();       }   }   finally {       try {           dbConn.setAutoCommit(true);       }       catch (SQLException _ex) {           _ex.printStackTrace();       }       finally {           DAMContext.freeConnection(dbConn);       }   }  

二、解决方案一
确保两个线程访问同一个数据库表的记录的顺序都是一致的。
因为:假设线程(一)目前处理的状况是已更新的为数据集A,剩下未更新的为数据集B;由于两线程访问数据库表的顺序是一致的,故必须是先访问数据集A中的记录,然后再访问数据集B中的记录,因此线程(二)肯定是先访问数据集A中的记录,而此时必须等待,直至线程(一)commit或rollback释放全部的“行锁”。

三、解决方案二
PreparedStatement updatePs = dbConn.prepareStatement(updateSql,          ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);  
修改为:
PreparedStatement updatePs = dbConn.prepareStatement(updateSql,          ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);  

ResultSet.CONCUR_UPDATABLE 表示sql语句是要做更新操作,那么在更新这批记录集前,就会先拿到"页锁";如果这批记录集的部分记录的“行锁”未释放,则等待这批记录的“行锁”全部释放,获取“页锁”。在“页锁”释放掉之前,别人不可能拿到这批记录集中的任何一条记录的“行锁”,也就不可能发生死锁。


你可能感兴趣的:(oracle)