2014-06-06 2 views
0

나는이 이상한 교착 상태를 받고 있어요하고자바 MySQL의 교착 상태 (멀티 스레딩)

을 발생하는 이유를 두 스레드가 거의 동시에이 하나의 방법을 호출하면 나는 확실히 알아낼 수 없습니다,이 예외가 :

java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction 

이 내가 모든 것을 동기화 된 블록 내부에 포장되어 있기 때문에 교착 상태가 발생할 수있는 방법을 이해하지 않는 방법

public void saveItems(List<Pair<Item, MapleInventoryType>> items, int id) throws    SQLException { 
    synchronized (this) { 
     PreparedStatement ps = null; 
     PreparedStatement pse = null; 
     try { 
      StringBuilder query = new StringBuilder(); 
      query.append("DELETE FROM `inventoryitems` WHERE `type` = ? AND `"); 
      query.append(account ? "accountid" : "characterid").append("` = ?"); 
      Connection con = DatabaseConnection.getConnection(); 
      ps = con.prepareStatement(query.toString()); 
      ps.setInt(1, value); 
      ps.setInt(2, id); 
      ps.executeUpdate(); //DEADLOCK OCCURS HERE 
      ps.close(); 

      for (Pair<Item, MapleInventoryType> pair : items) { 
       Item item = pair.getLeft(); 
       MapleInventoryType mit = pair.getRight(); 
       ps = con.prepareStatement("INSERT INTO `inventoryitems` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS); 
       ps.setInt(1, value); 
       ps.setString(2, account ? null : String.valueOf(id)); 
       ps.setString(3, account ? String.valueOf(id) : null); 
       ps.setInt(4, item.getItemId()); 
       ps.setInt(5, mit.getType()); 
       ps.setInt(6, item.getPosition()); 
       ps.setInt(7, item.getQuantity()); 
       ps.setString(8, item.getOwner()); 
       ps.setInt(9, item.getPetId()); 
       ps.setInt(10, item.getFlag()); 
       ps.setLong(11, item.getExpiration()); 
       ps.setString(12, item.getGiftFrom()); 
       ps.executeUpdate(); 

       if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) { 
        pse = con.prepareStatement("INSERT INTO `inventoryequipment` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); 
        try (ResultSet rs = ps.getGeneratedKeys()) { 

         if (!rs.next()) { 
          throw new RuntimeException("Inserting item failed."); 
         } 

         pse.setInt(1, rs.getInt(1)); 
        } 
        ps.close(); 

        Equip equip = (Equip) item; 
        pse.setInt(2, equip.getUpgradeSlots()); 
        pse.setInt(3, equip.getLevel()); 
        pse.setInt(4, equip.getStr()); 
        pse.setInt(5, equip.getDex()); 
        pse.setInt(6, equip.getInt()); 
        pse.setInt(7, equip.getLuk()); 
        pse.setInt(8, equip.getHp()); 
        pse.setInt(9, equip.getMp()); 
        pse.setInt(10, equip.getWatk()); 
        pse.setInt(11, equip.getMatk()); 
        pse.setInt(12, equip.getWdef()); 
        pse.setInt(13, equip.getMdef()); 
        pse.setInt(14, equip.getAcc()); 
        pse.setInt(15, equip.getAvoid()); 
        pse.setInt(16, equip.getHands()); 
        pse.setInt(17, equip.getSpeed()); 
        pse.setInt(18, equip.getJump()); 
        pse.setInt(19, 0); 
        pse.setInt(20, equip.getVicious()); 
        pse.setInt(21, equip.getItemLevel()); 
        pse.setInt(22, equip.getItemExp()); 
        pse.setInt(23, equip.getRingId()); 
        pse.executeUpdate(); 
        pse.close(); 
       } 
      } 
     } finally { 
      if (ps != null) { 
       ps.close(); 
      } 
      if (pse != null) { 
       pse.close(); 
      } 
     } 
} 

입니다.

도움을 주시면 대단히 감사하겠습니다.

답변

1

트랜잭션 격리 수준을 확인하십시오.

autoCommit()을 false로 설정 했습니까? true이면 트랜잭션 컨텍스트에서 실행 중일 때 트랜잭션 격리 수준이 Repeatable Read으로 강하면 커밋이 발생할 때까지 활성 트랜잭션의 컨텍스트에 Lock이 설정되므로 작업 후에 트랜잭션을 커밋해야합니다.

당신이하고있는 일에 대해 자세히 알려주십시오.

+0

오른쪽, autoCommit이이 이름이었습니다. +1 –

+0

autoCommit이 사용 설정되었습니다. –

+0

사실, 방금 문제점을 알았습니다. 이 메소드가 호출 된 컨텍스트에서 롤백 할 수 있으려면 autoCommit이 해제됩니다. –

0

모든 것이 동기화 된 블록 안에 래핑되므로 교착 상태가 발생할 수있는 방법을 이해할 수 없습니다.

예를 들어 다른 프로세스/스레드 동일한 테이블에 INSERT 귀하의 DB가 어떻게 구성되어 있는지에 따라 귀하는 귀하가 받고있는이 예외를받을 수 있습니다. 여기서 동기화 된 블록은 다른 Java 스레드가 동시에이 동일한 코드 블록을 호출하거나 입력 할 수 없다는 것을 의미합니다. 이것은 동일한 DB 리소스 테이블/페이지/레코드 (DB 수준)가 다른 프로세스/스레드에 의해 동시에 액세스되는 것을 방지하지 않습니다.

+0

// 여기에 DEADLOCK OCCURS HERE 전과 테이블에 액세스하는 다른 모든 장소 앞에 println 문을 추가하고 스레드 중 어느 것이 수행 중인지 확인하십시오. 또한 첫 번째 스레드가 DB 트랜잭션을 커밋했는지 확인하십시오. 두 번째 트랜잭션이 DB 리소스에 액세스하기 시작할 때 ... 그러나 문제는 두 스레드가 동시에 동기화 된 블록 안에 있지 않다는 것입니다. –

0

Connection에서 close()를 호출하지 않는 것으로 나타났습니다. finally {} 블록 대신이 방법을 사용해보십시오. 희망이 도움이됩니다.

finally { 
     if (ps != null) ps.close(); 
     if (pse != null) pse.close(); 
     if (con != null) con.close(); 
    } 
+0

연결이 나중에 다시 사용되므로 아직 닫지 않았습니다. –

0

인벤토리 설비 전에 인벤토리에 액세스하고 있습니다. 반대 순서로이 작업을 수행하는 또 다른 스레드가있어 교착 상태가 발생할 수 있습니다.

항상 잠금 계층 구조가 있어야하며 동일한 순서로 잠긴 리소스에만 액세스해야합니다.

ps.executeUpdate() 바로 뒤에 ps.close()를 사용하지 않는 이유는 무엇입니까? 복잡한 if 문 다음에? pse에서 ps의 값을 사용해야하는 경우 사이에 변수에 저장해야합니다.