Thursday, March 10, 2011

Concurrency and Performance (Intro)

In my last blog articles, which are unfortunately more than 4 months old, I wrote about an application I developed during my spare time. Lately I began
to migrate mshop from Spring 3 to JEE 6 for some reasons I may talk about in an upcoming article.

During the refactoring, I came across certain concurrency and performance topics which puzzled me for some time. In parallel a colleague of mine established a company interal workshop series about current software development topics. Since I do think that concurrency and performance are two fundamental topics in software engineering, I offered to prepare a little series about these subjects. As these presentations contain no company secrets, I decided to present them at this place as well. If you have got any ideas or topics you would like to read about, do not hesitate to contact me.

In order to make you aware of the subjects to come, I will present you a typical concurrency situation producing invalid data. Let's assume we have two actors A and B where both share a common bank account BA they try to withdraw money from. To map this into a software I used a very naive approach and modeled a bank account implementation as follows:


/**
* Provides an unsafe bank account implementation
*/
public class UnsafeBankAccount {

private int amount = 0;

/**
* Initializes the account
* @param initialAmount
*/
public UnsafeBankAccount(int initialAmount) {
this.amount = initialAmount;
}

/**
* Withdraws a named amount of money from the account. In
* case the withdrawal is allowed the method returns true
* otherwise false
* @param a
* @return
*/
public boolean withdrawMoney(int a) {

if(a < amount) {
amount = amount - a;
return true;
}

return false;
}

/**
* Returns the current amount of money stored
* in this account
* @return
*/
public int getAmount() {
return amount;
}

}



In a single-threaded environment this implementation would cause no trouble since it is accessed in a sequential manner. If you tend to use the UnsafeBankAccount in a multi-threaded setting, you might observe inconsistent states between the results of UnsafeBankAccount#withdrawMoney and UnsafeBankAccount#getAmount.

Assume the following situation: the actors A and B share a common bank account BA they wish to withdraw money from. At first each checks the current state of the account calling UnsafeBankAccount#getAmount and then decide to withdraw a certain amount of money without overchecking the account. What could happen is:


  • The initial amount of BA is $100

  • A checks the account and sees that the account holds $100

  • A orders to withdraw $30 from the account

  • While the software validates the condition of the if-clause in UnsafeBankAccount#withdrawMoney to true the scheduler interrupts the computation and hands over the control to the thread of B

  • B checks the account as well and also sees that the account holds $100

  • B orders to withdraw $80 from the account

  • The execution thread handling the order of B as well validates the if-clause in UnsafeBankAccount#withdrawMoney to true, recalculates the current state of the account and returns true

  • Now the execution thread handling the order of A also recalculates the current state of the account and returns true as well

  • If any of the customers checks BA, they will see that the account is overchecked by $10



A different situation could be:


  • The initial amount of BA is $100

  • A orders to withdraw $30 from the account

  • Before UnsafeBankAccount#withdrawMoney has the chance to update the variable amount, the scheduler interrupts the current thread

  • Now B checks the current account state by calling UnsafeBankAccount#getAmount and sees that the account still holds $100



To ensure that the bank account works as expected in both cases, we need to ensure that (1) the amount variable is always in a consistent state and (2) each call returning the amount sees the most current value.
The magic element in this case is the synchronized modifier which will be used to guard the reading and writing accesses to amount. This leads to the following modification:


/**
* Provides a thread-safe implementation of a bank account
*/
public class ThreadSafeBankAccount {

private Integer amount = null;

/**
* Initializes the account
* @param intialAmount
*/
public ThreadSafeBankAccount(int intialAmount) {
this.amount = Integer.valueOf(intialAmount);
}

/**
* Withdraws a named amount of money from the account. In
* case the withdrawal is allowed the method returns true
* otherwise false
* @param a
* @return
*/
public boolean withdrawMoney(int a) {

synchronized(amount) {
if(a < amount) {
amount = amount - a;
return true;
}

return false;
}
}

/**
* Returns the current amount of money stored
* in this account
* @return
*/
public int getAmount() {
synchronized(amount) {
return amount;
}
}

}



The implementation above ensures that no read or write access takes place while another thread reads or writes the amount variable. Depending on the use case mapped by the implementation, the synchronisation used in the getter method could be omitted, eg. the caller does not wish to retrieve the latest and most current value.