Reputation: 1721
I'm getting this weird deadlock and I can't quite figure out why it occurs
If two threads call this one method at about the same time, I get this exception:
java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
This is the method
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();
}
}
}
I don't understand how the deadlock could occur since everything is wrapped inside the synchronized block.
Help would be greatly appreciated, thanks.
Upvotes: 0
Views: 683
Reputation: 414
Check your transaction isolation level.
Did you set autoCommit() to false ? If true, then when you're running in transaction's context, you need to commit it after work, because, if your transaction isolation level is strong as Repeatable Read
, then Lock is on active transaction's context until commit occurs.
Give us more details about what you're doing.
Upvotes: 1
Reputation: 1
You are accessing inventoryitems before inventoryequipment. There might be another thread doing this in the opposite order, leading to a deadlock.
You should always have a hierarchy of locks and only access locked resources in the same order.
Why don't you use ps.close() directly after ps.executeUpdate(); but after a complicated if statement? If you need to use values from ps in pse, you should store them in variables between.
Upvotes: 0
Reputation: 3533
I noticed that you're not calling close() on your Connection. Try using this in place of your finally { } block. Hope this helps.
finally {
if (ps != null) ps.close();
if (pse != null) pse.close();
if (con != null) con.close();
}
Upvotes: 0
Reputation: 39437
I don't understand how the deadlock could occur since everything is wrapped inside the synchronized block.
You may have e.g. another process/thread doing e.g. an INSERT in the same table. Depending on how your DB is configured you may get this exception which you're getting. The synchronized block here just means that no other Java thread can call/enter into this same block of code at the same time. This doesn't prevent the same DB resource table/page/record (on the DB level) from being accessed by another process/thread at the same time.
Upvotes: 0