// This is thread-safe and cannot deadlock; takes the locks in the // order determined by the Account object's serial number. public void transferD(Account that, final long amount) { Account ac1 = this, ac2 = that; if (ac1.serial <= ac2.serial) synchronized (ac1) { synchronized (ac2) { // ac1 < ac2 ac1.balance = ac1.balance - amount; ac2.balance = ac2.balance + amount; } } else synchronized (ac2) { synchronized (ac1) { // ac2 < ac1 ac1.balance = ac1.balance - amount; ac2.balance = ac2.balance + amount; } } }
private void createAccounts() throws IOException, DeadlockException, InterruptedException, TransactionRolledBackException { for (int a = 0; a < nAccounts; a++) { Account account = new Account(new AccountId(a)); account.balance(0); accounts.ensurePresent(account); } db.commitTransaction(); }
// This is thread-safe but may deadlock; takes the locks in the // order determined by the Account object's hashCode. May deadlock // in (the rare) case distinct objects get identical hashcodes; this // case may be handled using a third lock, as in Goetz p. 209. public void transferE(Account that, final long amount) { Account ac1 = this, ac2 = that; int ac1Hash = System.identityHashCode(ac1); int ac2Hash = System.identityHashCode(ac2); if (ac1Hash < ac2Hash) synchronized (ac1) { synchronized (ac2) { // ac1 < ac2 ac1.balance = ac1.balance - amount; ac2.balance = ac2.balance + amount; } } else if (ac1Hash > ac2Hash) synchronized (ac2) { synchronized (ac1) { // ac2 < ac1 ac1.balance = ac1.balance - amount; ac2.balance = ac2.balance + amount; } } else synchronized (tieLock) { synchronized (ac1) { synchronized (ac2) { // ac1 < ac2 ac1.balance = ac1.balance - amount; ac2.balance = ac2.balance + amount; } } } }
private void verify() throws IOException, InterruptedException { long sum = 0; Cursor cursor = accounts.first(); Account account; while ((account = (Account) cursor.next()) != null) { LOG.log(Level.INFO, "{0}", account); sum += account.balance(); } db.commitTransaction(); if (sum == 0) { LOG.log(Level.INFO, "OK!"); } else { LOG.log(Level.WARNING, "Test failed, sum = {0}", sum); } }
public static void main(String[] args) { Account mike; mike = new Account(1000); System.out.println(mike.balance()); mike.deposit(100); System.out.println(mike.balance()); mike.withdraw(200); System.out.println(mike.balance()); // overdraft test Account Tom; Tom = new Account(2000); Account Kate; Kate = new Account(1000, Tom); Kate.withdraw(1001); if (Kate.balance() == 0 && Tom.balance() == 1999) { } else { System.out.println("Error: Overdraft test failed"); } // withdraw boolean tests Account peter = new Account(90); boolean bool1 = peter.withdraw(-5); boolean bool2 = peter.withdraw(100); boolean bool3 = peter.withdraw(50); if (!bool1 && !bool2 && bool3) { } else { System.out.println("Error: Withdraw failed"); } // merge tests Account tyler = new Account(500); Account bob = new Account(900); tyler.merge(bob); if (tyler.balance() == 1400 && bob.balance() == 0) { } else { System.out.println("Error: Merge test failed"); } } // end main
public void run() { int deadlockAborts = 0; int conflictAborts = 0; try { for (int i = 0; i < nTransactions; i++) { boolean committed = false; StringBuilder description = new StringBuilder(); do { transaction = transaction(); int from; int to; do { from = random.nextInt(nAccounts); to = random.nextInt(nAccounts); } while (from == to); int amount = MIN_AMOUNT + random.nextInt(MAX_AMOUNT); AccountId fromAccountId = new AccountId(from); AccountId toAccountId = new AccountId(to); try { Account fromAccount = account(fromAccountId); Account toAccount = account(toAccountId); if (LOG.isLoggable(Level.INFO)) { LOG.log( Level.INFO, "{0}: Attempt transfer of {1} from {2} to {3}", new Object[] {transaction, amount, fromAccountId, toAccountId}); } description.setLength(0); description.append(String.format("%s, %s", fromAccount, toAccount)); fromAccount.balance(fromAccount.balance() - amount); toAccount.balance(toAccount.balance() + amount); description.append(String.format(" -> %s, %s", fromAccount, toAccount)); accounts.ensurePresent(fromAccount); accounts.ensurePresent(toAccount); db.commitTransaction(transactionCallback); committed = true; if (LOG.isLoggable(Level.INFO)) { LOG.log( Level.INFO, "{0}: Transferred {1}: {2}", new Object[] {transaction, amount, description}); } } catch (com.geophile.erdo.transaction.DeadlockException e) { LOG.log( Level.INFO, "{0}: transfer of {1} from {2} to {3} failed due to deadlock", new Object[] {transaction, amount, fromAccountId, toAccountId}); deadlockAborts++; } catch (TransactionRolledBackException e) { LOG.log( Level.INFO, "{0}: transfer of {1} from {2} to {3} failed due to conflict", new Object[] {transaction, amount, fromAccountId, toAccountId}); conflictAborts++; } } while (!committed); } } catch (Throwable th) { LOG.log(Level.SEVERE, "Caught exception", th); termination = th; } finally { LOG.log( Level.INFO, "{0} transactions, {1} deadlock aborts, {2} conflict aborts, termination: {3}", new Object[] { nTransactions, deadlockAborts, conflictAborts, (termination == null ? null : termination.getMessage()) }); } }
public Account createAccount(BigDecimal balance) { Account account = new Account(); account.balance = balance; account.name = generateString(new Random(), "Test name", 10); return account; }