簡體   English   中英

Java MySQL死鎖(多線程)

[英]Java MySQL deadlock (multithreading)

我遇到了這個怪異的僵局,我不知道為什么會發生

如果兩個線程大約同時調用此方法,則會出現此異常:

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();
            }
        }
}

我不明白死鎖是怎么發生的,因為所有內容都包裝在同步塊中。

非常感謝您的幫助。

檢查您的事務隔離級別。

您是否將autoCommit()設置為false? 如果為true,則當您在事務上下文中運行時,需要在工作后提交它,因為,如果您的事務隔離級別很強,如Repeatable Read ,則Lock處於活動事務上下文中,直到提交為止。

向我們提供有關您正在做的事情的更多詳細信息。

我不明白死鎖是怎么發生的,因為所有內容都包裝在同步塊中。

您可能有另一個進程/線程在同一個表中執行例如INSERT。 根據數據庫的配置方式,可能會遇到此異常。 這里的同步塊僅意味着沒有其他Java線程可以同時調用/輸入相同的代碼塊。 這不會阻止同一數據庫資源表/頁面/記錄(在數據庫級別)同時被另一個進程/線程訪問。

我注意到您沒有在Connection上調用close()。 嘗試用它代替您的finally {}塊。 希望這可以幫助。

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

您在庫存設備之前訪問庫存項目。 可能會有另一個線程以相反的順序執行此操作,從而導致死鎖。

您應該始終具有鎖層次結構,並且只能以相同的順序訪問鎖定的資源。

為什么不在ps.executeUpdate()之后直接使用ps.close()? 但是經過復雜的if語句之后? 如果需要在pse中使用ps中的值,則應將它們存儲在之間的變量中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM