A ReentrantLock Example
Home ] Up ] Lock Objects ] ReentrantLock ] [ A ReentrantLock Example ] Finer Granularity ] Condition Objects ] Explicit Locks vs. synchronized ] Read/Write Locks ]

 

 

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.

 
The page was last updated February 19, 2008