An Example
Home ] Up ] The Condition Interface ] await() ] signal() & signalAll() ] [ An Example ]

 

 

Here's an example of the use of condition objects:

package threads;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
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);
    Account a3 = new Account("Account 3", 5);
    System.out.println("Initial Balances:");
    System.out.println(" Account 1: " + a1.getBalance());
    System.out.println(" Account 2: " + a2.getBalance());
    System.out.println(" Account 3: " + a3.getBalance());
    System.out.println("Transferring 100 from Account 3 to Account 1");
    System.out.println("Transferring 5 from Account 1 to Account 2");
    System.out.println("Transferring 20 from Account 2 to Account 1");
    System.out.println("Transferring 500 from Account 2 to Account 3");
    Thread threadX = 
        new TransferFundsThread("Thread X", a3, a1, 100);
    Thread threadA =
        new TransferFundsThread("Thread A", a1, a2, 5);
    Thread threadB =
        new TransferFundsThread("Thread B", a2, a1, 20);
    Thread threadY =
        new TransferFundsThread("Thread Y", a2, a3, 500);
    threadX.start();
    threadA.start();
    threadB.start();
    threadY.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;
  }
  
  public Lock getLock()
  {
    return m_lock;
  }
  
  public void awaitSufficientFunds(int amount) throws InterruptedException
  {
    System.out.println(getName() + " awaiting sufficient funds for " + amount);
    m_sufficientFunds.await(500, TimeUnit.MILLISECONDS);
  }
  
  public void signalBalanceChanged()
  {
    m_sufficientFunds.signalAll();
  }
  
  private String    m_name;
  private int       m_balance;
  private Lock      m_lock = new ReentrantLock();
  private Condition m_sufficientFunds = m_lock.newCondition();
}

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()
  {
    System.out.println("run: Transferring funds from " + m_from.getName() +
                       " to " + m_to.getName());
    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()
  {
    while (true)
    {
      try
      {
        // Try to acquire the lock for the from account
        if ( m_from.getLock().tryLock(500, TimeUnit.MILLISECONDS) )
        {
          try
          {
            // See if there are sufficient funds in the from account
            while (getBalance() < m_amount)
            {
              m_from.awaitSufficientFunds(m_amount);
            }
            
            // Try to acquire the lock for the to account
            if ( m_to.getLock().tryLock(500, TimeUnit.MILLISECONDS) )
            {
              try
              {
                doTransfer( getBalance() );
              }
              finally
              {
                // Unlock the to lock, regardless of whether
                // an exception occurred.
                m_to.getLock().unlock();
              }
              // We succeeded, so break out of the for while loop.
              break;
            }
          }
          finally
          {
            // Unlock the from lock, regardless of whether
            // an exception occurred.
            m_from.getLock().unlock();
          }
        }
      }
      catch (InterruptedException ie)
      {
        // Do nothing; try again
      }
    }
  }
  
  /**
   * Get the balance from the to account
   */
  private int getBalance()
  {
    System.out.println(getName() +
        ": Getting balance from " +
        m_from.getName());
    System.out.println(getName() +
        ": Balance from " +
        m_from.getName() +
        " = " + m_from.getBalance());
    return m_from.getBalance();
  }
  
  /**
   * Do the actual transfer of funds
   */
  private void doTransfer(int fromBalance)
  {
    System.out.println(getName() +
        ": Getting balance from " +
        m_to.getName());
    int toBalance = m_to.getBalance();
    System.out.println(getName() +
        ": Balance from " +
        m_to.getName() +
        " = " + toBalance);
    
    if (fromBalance >= m_amount)
    {
      System.out.println(getName() +
          ": Withdrawing funds from " +
          m_from.getName());
      m_from.setBalance(fromBalance - m_amount);
      System.out.println(getName() +
          ": Depositing funds into " +
          m_to.getName());
      m_to.setBalance(toBalance + m_amount);
      
      // Indicate to any awaiters on the to account that the balance has changed.
      m_to.signalBalanceChanged();
      
      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;
}

This, when run, produces the following output:

Initial Balances:
 Account 1: 10000
 Account 2: 30000
 Account 3: 5
Transferring 100 from Account 3 to Account 1
Transferring 5 from Account 1 to Account 2
Transferring 20 from Account 2 to Account 1
Transferring 500 from Account 2 to Account 3
run: Transferring funds from Account 3 to Account 1
Thread X: Getting balance from Account 3
Thread X: Balance from Account 3 = 5
Account 3 awaiting sufficient funds for 100
run: Transferring funds from Account 1 to Account 2
Thread A: Getting balance from Account 1
run: Transferring funds from Account 2 to Account 1
run: Transferring funds from Account 2 to Account 3
Thread A: Balance from Account 1 = 10000
Thread B: Getting balance from Account 2
Thread B: Balance from Account 2 = 30000
Thread X: Getting balance from Account 3
Thread X: Balance from Account 3 = 5
Account 3 awaiting sufficient funds for 100
Thread A: Getting balance from Account 1
Thread A: Balance from Account 1 = 10000
Thread B: Getting balance from Account 2
Thread B: Balance from Account 2 = 30000
Thread X: Getting balance from Account 3
Thread X: Balance from Account 3 = 5
Account 3 awaiting sufficient funds for 100
Thread B: Getting balance from Account 2
Thread B: Balance from Account 2 = 30000
Thread B: Getting balance from Account 1
Thread B: Balance from Account 1 = 10000
Thread B: Withdrawing funds from Account 2
Thread B: Depositing funds into Account 1
Thread B: New balances are:
  Account 2 = 29980
  Account 1 = 10020
Thread A: Getting balance from Account 1
Thread A: Balance from Account 1 = 10020
Thread A: Getting balance from Account 1
Thread A: Balance from Account 1 = 10020
Thread A: Getting balance from Account 2
Thread A: Balance from Account 2 = 29980
Thread A: Withdrawing funds from Account 1
Thread A: Depositing funds into Account 2
Thread A: New balances are:
  Account 1 = 10015
  Account 2 = 29985
Thread Y: Getting balance from Account 2
Thread Y: Balance from Account 2 = 29985
Thread Y: Getting balance from Account 2
Thread Y: Balance from Account 2 = 29985
Thread Y: Getting balance from Account 3
Thread Y: Balance from Account 3 = 5
Thread Y: Withdrawing funds from Account 2
Thread Y: Depositing funds into Account 3
Thread Y: New balances are:
  Account 2 = 29485
  Account 3 = 505
Thread X: Getting balance from Account 3
Thread X: Balance from Account 3 = 505
Thread X: Getting balance from Account 3
Thread X: Balance from Account 3 = 505
Thread X: Getting balance from Account 1
Thread X: Balance from Account 1 = 10015
Thread X: Withdrawing funds from Account 3
Thread X: Depositing funds into Account 1
Thread X: New balances are:
  Account 3 = 405
  Account 1 = 10115
 
The page was last updated February 19, 2008