public class Bank {
/**
 * argv[0]: number of concurrently executed threads
 * argv[1]: number of managed accounts
*/
	
	public static void main(String argv[]) {
		int noAccounts = Integer.parseInt(argv[1]);
		Account accounts[] = new Account[noAccounts];
		for (int i=0; i<noAccounts; i++)
			accounts[i] = new Account(Math.random()*1000);
		
		int fromAccount;
		int toAccount;

		for (int i=0; i<Integer.parseInt(argv[0]); i++) {
			 fromAccount = (int) (Math.random()*noAccounts);
			 do {
			 	toAccount = (int) (Math.random()*noAccounts);
			 } while (toAccount == fromAccount);
			 	
			new Thread( new Transferal(accounts, fromAccount, toAccount, Math.random()*1000)).start();
		} //for
	} //main()	
} //class Bank

class Account {
	double balance;
	
	public Account(double init) {
		balance = init;
	} //constructor
	
	public void change(double amount) {
		balance += amount;
	} //change()
} //class Account

class Transferal implements Runnable {
	Account accounts[];
	int fromAccount;
	int toAccount;
	double amount;
	
	public Transferal(Account accounts[], int fromAccount, int toAccount, double amount) {
		this.accounts = accounts;
		this.fromAccount = fromAccount;
		this.toAccount = toAccount;
		this.amount = amount;
	} //constructor
	
	public void run() {
		System.out.println("("+Thread.currentThread().getName()+") accounts["+fromAccount+"] --"+amount+"--> accounts["+toAccount+"] started");			
		synchronized( accounts[fromAccount] ) {
			System.out.println("("+Thread.currentThread().getName()+") account["+fromAccount+"] locked (fromAccount)");
			synchronized( accounts[toAccount] ) {
				System.out.println("("+Thread.currentThread().getName()+") account["+toAccount+"] locked (toAccount");
				try {
					Thread.sleep( (int) (Math.random() * 200) );
				} //try
				catch (InterruptedException ie) {
					System.out.println("An InterruptedException caught\n"+ie.getMessage());
					ie.printStackTrace();
					System.exit(1);
				} //catch()				
				accounts[toAccount].change(amount);
				accounts[fromAccount].change(-amount);
			} //synchronized
			System.out.println("("+Thread.currentThread().getName()+") account["+toAccount+"] released");			
		} //synchronized
		System.out.println("("+Thread.currentThread().getName()+") account["+fromAccount+"] released");
		System.out.println("("+Thread.currentThread().getName()+") accounts["+fromAccount+"] --"+amount+"--> accounts["+toAccount+"] finished");
	} //run()
} //class Transferal