import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
 * DeadlockAvoidance
 * <p/>
 * Avoiding lock-ordering deadlock using tryLock
 *
 */
public class DeadlockAvoidance {
    private static final int NUM_THREADS = 20;
    private static final int NUM_ACCOUNTS = 5;
    private static final int NUM_ITERATIONS = 100;
    private static final int MAX_MONEY = 1000;
    private static final long TIMEOUT = 100;
    private static final TimeUnit TIMEUNITS = NANOSECONDS;
    public static void main(String[] args) {
        final Random rnd = new Random();
        final Account[] accounts = new Account[NUM_ACCOUNTS];
        for (int i = 0; i < accounts.length; i++)
            accounts[i] = new Account(rnd.nextInt(MAX_MONEY));
        class TransferThread extends Thread {
            public void run() {
                for (int i = 0; i < NUM_ITERATIONS; i++) {
                    int fromAcct = rnd.nextInt(NUM_ACCOUNTS);
                    int toAcct = rnd.nextInt(NUM_ACCOUNTS);
                    DollarAmount amount = new DollarAmount(rnd.nextInt(1000));
                    try {
                        DeadlockAvoidance.transferMoney(accounts[fromAcct], accounts[toAcct], amount, TIMEOUT, TIMEUNITS);
                    } catch (DeadlockAvoidance.InsufficientFundsException ignored) {
                        //System.out.println("no money left.");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //System.out.println("transfered " + amount.amount);
                }
            }
        }
        for (int i = 0; i < NUM_THREADS; i++)
            new TransferThread().start();
        
        System.out.println("Done.");
    }
    private static Random rnd = new Random();
    public static boolean transferMoney(Account fromAcct,
                                 Account toAcct,
                                 DollarAmount amount,
                                 long timeout,
                                 TimeUnit unit)
            throws InsufficientFundsException, InterruptedException {
        long fixedDelay = getFixedDelayComponentNanos(timeout, unit);
        long randMod = getRandomDelayModulusNanos(timeout, unit);
        long timeoutNanos = unit.toNanos(timeout);
        long startTime = System.nanoTime();
        while (true) {
            if (fromAcct.lock.tryLock()) {
                try {
                    if (toAcct.lock.tryLock()) {
                        try {
                            if (fromAcct.getBalance().compareTo(amount) < 0)
                                throw new InsufficientFundsException();
                            else {
                                fromAcct.debit(amount);
                                toAcct.credit(amount);
                                return true;
                            }
                        } finally {
                            toAcct.lock.unlock();
                        }
                    }
                } finally {
                    fromAcct.lock.unlock();
                }
            }
            if (System.nanoTime() - startTime >= timeoutNanos)
                return false;
            NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
        }
    }
    private static final int DELAY_FIXED = 1;
    private static final int DELAY_RANDOM = 2;
    static long getFixedDelayComponentNanos(long timeout, TimeUnit unit) {
        return DELAY_FIXED;
    }
    static long getRandomDelayModulusNanos(long timeout, TimeUnit unit) {
        return DELAY_RANDOM;
    }
    static class DollarAmount implements Comparable<DollarAmount> {
        int amount;
        public DollarAmount(int amount) {
            this.amount = amount;
        }
        public DollarAmount add(DollarAmount d) {
            this.amount += d.amount;
            return this;
        }
        public DollarAmount subtract(DollarAmount d) {
            this.amount -= d.amount;
            return this;
        }
        public int compareTo(DollarAmount dollarAmount) {
            return this.amount - dollarAmount.amount;
        }
    }
    static class Account {
        public Lock lock;
        private DollarAmount balance;
        private final int acctNo;
        private static final AtomicInteger sequence = new AtomicInteger();
        public Account(int money) {
            acctNo = sequence.incrementAndGet();
            balance = new DollarAmount(money);
            lock = new ReentrantLock();
        }
        void debit(DollarAmount d) {
            balance = balance.subtract(d);
        }
        void credit(DollarAmount d) {
            balance = balance.add(d);
        }
        DollarAmount getBalance() {
            return balance;
        }
        int getAcctNo() {
            return acctNo;
        }
    }
    static class InsufficientFundsException extends Exception {
    }
}
No comments:
Post a Comment