package threads.deadlock; import java.util.ArrayList; import java.util.List; import java.util.Random; public class AvoidsDeadlockDemo { private static final int NUM_ACCOUNTS = 10; private static final int NUM_THREADS = 20; private static final int NUM_ITERATIONS = 100000; static final Random rnd = new Random(); List<Account> accounts = new ArrayList<Account>(); public static void main(String args[]) { AvoidsDeadlockDemo demo = new AvoidsDeadlockDemo(); demo.setUp(); demo.run(); } void setUp() { for (int i = 0; i < NUM_ACCOUNTS; i++) { Account account = new Account(i, rnd.nextInt(1000)); accounts.add(account); } } void run() { for (int i = 0; i < NUM_THREADS; i++) { new BadTransferOperation(i).start(); } } class BadTransferOperation extends Thread { int threadNum; BadTransferOperation(int threadNum) { this.threadNum = threadNum; } @Override public void run() { for (int i = 0; i < NUM_ITERATIONS; i++) { Account toAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS)); Account fromAccount = accounts.get(rnd.nextInt(NUM_ACCOUNTS)); int amount = rnd.nextInt(1000); if (!toAccount.equals(fromAccount)) { try { transfer(fromAccount, toAccount, amount); System.out.print("."); } catch (OverdrawnException e) { System.out.print("-"); } if (i % 60 == 0) { System.out.print("\n"); } } } System.out.println("Thread Complete: " + threadNum); } /** * This is the crucial point here. The idea is that to avoid deadlock * you need to ensure that threads can't try to lock the same two * accounts in the same order */ private void transfer(Account fromAccount, Account toAccount, int transferAmount) throws OverdrawnException { if (fromAccount.getNumber() > toAccount.getNumber()) { synchronized (fromAccount) { synchronized (toAccount) { fromAccount.withDrawAmount(transferAmount); toAccount.deposit(transferAmount); } } } else { synchronized (toAccount) { synchronized (fromAccount) { fromAccount.withDrawAmount(transferAmount); toAccount.deposit(transferAmount); } } } } } }