|
| | Here's an example of the use of a ReentrantLock as one solution to the Bank
Account transfer deadlock situation:
package threads;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* This class will use an explicit lock to avoid a deadlock situation.
*/
public class ExplicitLockAccountTransfer
{
public static void main(String[] args)
{
Account a1 = new Account("Account 1", 10000);
Account a2 = new Account("Account 2", 30000);
System.out.println("Initial Balances:");
System.out.println(" Account 1: " + a1.getBalance());
System.out.println(" Account 2: " + a2.getBalance());
System.out.println("Transferring 5 from Account 1 to Account 2");
System.out.println("Transferring 20 from Account 2 to Account 1");
Thread threadA =
new TransferFundsThread("Thread A", a1, a2, 5);
Thread threadB =
new TransferFundsThread("Thread B", a2, a1, 20);
threadA.start();
threadB.start();
}
}
class Account
{
public Account(String name, int balance)
{
m_name = name;
m_balance = balance;
}
public int getBalance()
{
return m_balance;
}
public void setBalance(int newBalance)
{
m_balance = newBalance;
}
public String getName()
{
return m_name;
}
private String m_name;
private int m_balance;
}
class TransferFundsThread extends Thread
{
public TransferFundsThread(String name,
Account from,
Account to,
int amount)
{
super(name);
m_from = from;
m_to = to;
m_amount = amount;
}
public void run()
{
transferFunds();
}
/**
* In order to transfer funds safely, we need to be able
* to hold a lock on the single lock used to synchronize
* access to the transferFunds method.
*/
private void transferFunds()
{
m_lock.lock();
try
{
doTransfer();
}
finally
{
// Unlock the lock, regardless of whether
// an exception occurred.
m_lock.unlock();
}
}
/**
* Do the actual transfer of funds
*/
private void doTransfer()
{
System.out.println(getName() +
": Getting balance from " +
m_from.getName());
int balance1 = m_from.getBalance();
System.out.println(getName() +
": Balance from " +
m_from.getName() +
" = " + balance1);
System.out.println(getName() +
": Getting balance from " +
m_to.getName());
int balance2 = m_to.getBalance();
System.out.println(getName() +
": Balance from " +
m_to.getName() +
" = " + balance2);
if (balance1 >= m_amount)
{
System.out.println(getName() +
": Withdrawing funds from " +
m_from.getName());
m_from.setBalance(balance1 - m_amount);
System.out.println(getName() +
": Depositing funds into " +
m_to.getName());
m_to.setBalance(balance2 + m_amount);
System.out.println(getName() +
": New balances are:\n" +
" " + m_from.getName() +
" = " +
m_from.getBalance() + "\n" +
" " + m_to.getName() + " = " +
m_to.getBalance() );
}
}
/// Private data ///
private Account m_from;
private Account m_to;
private int m_amount;
private Lock m_lock = new ReentrantLock();
}
|
Which, when run produces the following output:
Initial Balances:
Account 1: 10000
Account 2: 30000
Transferring 5 from Account 1 to Account 2
Transferring 20 from Account 2 to Account 1
Thread A: Getting balance from Account 1
Thread A: Balance from Account 1 = 10000
Thread A: Getting balance from Account 2
Thread A: Balance from Account 2 = 30000
Thread A: Withdrawing funds from Account 1
Thread A: Depositing funds into Account 2
Thread A: New balances are:
Account 1 = 9995
Account 2 = 30005
Thread B: Getting balance from Account 2
Thread B: Balance from Account 2 = 30005
Thread B: Getting balance from Account 1
Thread B: Balance from Account 1 = 9995
Thread B: Withdrawing funds from Account 2
Thread B: Depositing funds into Account 1
Thread B: New balances are:
Account 2 = 29985
Account 1 = 10015
Notice, however, that this essentially forces each thread to wait until the
other thread has completely finished.
|