Click here to Skip to main content
Click here to Skip to main content

Session locks in multi-threaded programming

, 29 Jan 2004
Rate this:
Please Sign up or sign in to vote.
An article about synchronizing threads at a session level

Background

There is an article I have read once about Java's inability to synchronize at a session level. This was the case: Two worker threads hold a reference to a single BankAccount object. The BankAccount class is defined like so.

public class BankAccount {
  private double _amount;
  
  public synchronized void deposit(double amount) { 
    _amount += amount;
  }
  
  public synchronized void withdraw(double amount) { 
    _amount -= amount; 
  }
  
  public synchronized double getBalance() { 
    return _amount; 
  } 
}

One thread uses the object to deposit $1000 and requests the current balance immediately afterwards. The balance returned was surprisingly less than $1000. What happened is that the thread had switched between method calls, giving the other thread an opportunity to withdraw a sum of money.

account.deposit(1000.00);
// thread switches and the other thread withdraws some money
double Balance = account.getBalance();

There is no simple way in Java to prevent this from happening. You may think that the use of a synchronization block would be the answer.

synchronized(account) {
  account.deposit(1000.00);
  // thread switches but the other thread cannot withdraws money
  double Balance = account.getBalance(); 
}

This approach does not work because the other thread would block only if it passed through the same 'critical section' of code. It, however, passes through another section of code and therefore can still withdraw the money.

Java synchronization has no effect across multiple method calls. Java does not support the concept of a session lock, a locking mechanism that synchronizes across multiple method calls. This is how I would imagine it to work.

account.lock(); // lock this session across all threads
account.deposit(1000.00);
double Balance = account.getBalance();
account.unlock(); // unlock this session across all threads

You may refer to the book ['Java Threads', Scott Oaks & Henry Wong, O'REILLY, Chapter 3] to find out how much extra programming is needed to implement a session lock. Pages 53/53 describe a BusyFlag class.

Session Lock

A session lock is very useful especially when enumerating or iterating through all the elements in a collection. Fortunately, the .NET collection classes support the concept of a session lock. Here is an illustration:

// create a synchronized version of a collection object
ArrayList list = ArrayList.Synchronized( new ArrayList() );

// fill it with elements
for(int i=0; i<20; i++)
  list.Add( new BankAccount() );

// inside a thread procedure
int num = 0;
lock(list.SyncRoot) // this is a session lock
{
  double Sum=0;
  IEnumerator enm = list.GetEnumerator();
  while(enm.MoveNext())
    Sum += ((BankAccount)enm.Current).Balance;
    
  Console.Out.WriteLine("Sum: {0}", list.Count, num);
}

Every .NET collection class has this SynRoot member. If a lock is applied on this member, then every other thread is prevented from modifying the collection. No elements inside the collection can be removed or even modified. And no elements can be added to that collection. The SyncRoot is really a very good feature. But it is only available with the collection classes.

Nevertheless, we can provide session lock capabilities to any class of our own design with just a few lines of code. Unlike Java, the .NET framework provides us with a Monitor object which is a kind of critical section or mutex. Let us rewrite the BankAccount class in C#.

public class BankAccount {
  private double _amount;
  
  // the account methods
  public void deposit(double amount) {
    lock(this) {
      _amount += amount;
    }
  }
  public void withdraw(double amount) {
    lock(this) {
      _amount -= amount;
    }
  }
  public double Balance {
    get {
      lock(this) {
        return _amount;
      }
    }
  }
  
  
  // here are the session lock methods
  public lock() {
    Monitor.Enter(this);
  }
  public unlock() {
    Monitor.Exit();
  }
}

That is all. Thanks to the Monitor, we can lock the bank account across multiple method calls just as we have demonstrated above with the Java code.

The .NET Monitor is not really a critical section or mutex type of object. It in fact holds a lock on an object and monitors it across multiple threads. Until the lock is released, we can call multiple methods without the interference of another thread. Note, it does not prevent a thread switch but the other thread will be blocked from interfering.

If you look carefully at how I have used it in the BankAccount class, then you can readily see that the following code is equivalent:

Monitor.Enter(account); 
// equivalent to account.lock() { Monitor.Enter(this); }
account.deposit(1000.00); 
double Balance = account.Balance;
Monitor.Leave(account); 
// equivalent to account.unlock() { Monitor.Exit(this); }

FYI: The C# keyword lock is just a C# thing. The CLR does not understand it. The C# compiler translates lock to something like this:

try
{
  Monitor.Enter(account);
  // put your code here
}
finally
{
  Monitor.Exit(account);
}

The good news here is that we can apply a session lock in the more familiar way.

lock(account) {
  account.deposit(1000.00); 
  double Balance = account.Balance;
}

When writing multi-threaded programs, it is helpful to keep the Monitor in mind. For example, you may want to calculate the total sum of money in a list of bank accounts. Applying a lock on the SyncRoot before iterating through the list and adding the balances of each account would only work if no BankAccount object was referenced outside the list. To prevent a change to any of the bank accounts, you may want to use the Monitor.

lock(list.SyncRoot)
{
  // lock each object in the list
  foreach(Object obj in list)
    Monitor.Enter(obj);
   
  // compute the total sum  
  double Sum = 0;
  IEnumerator enm = list.GetEnumerator();
  while(enm.MoveNext())
    Sum += ((BankAccount)enm.Current).Balance;
  
  // unlock each object in the list
  foreach(Object obj in list)
    Monitor.Exit(obj);
}

When writing multi-threaded applications, it is important to distinguish between critical section type and monitor type of synchronizations. The .NET Monitor object is not the same as a common mutex or critical section. But you can use the Monitor to achieve a critical section kind of effect. Here is an example:

public class BankAccount {

  // ...
  
  // synchronizing on a private object achieves
  // a critical section kind of effect
  private Object balance = new Object();
  public double Balance {
    get {
      lock(balance) {
        return _amount;
      }
    }
  }
}

The trick is to reserve a private member as a synchronization reference. In general, you should design your synchronization around each data member of your class. Because the BankAccount is a small class, we have taken the object itself as a synchronization reference. If the class was more complex, a better approach would be like so:

public class BankAccount {
  private double _amount;

  // just to be the synchronization reference for '_amount'
  public Object Amount = new Object(); 
 
  public void deposit(double amount) {
    lock(Amount) {
      _amount += amount;
    }
  }
  
  public void withdraw(double amount) {
    lock(Amount) {
      _amount -= amount;
    }
  }
  
  // ....
}

Now we just need to lock the synchronization reference, Amount.

lock(account.Amount) {
    account.deposit();
    double Balance = account.Balance;
}

The attached project

You may download the attached project and play around with it. It is a simple console app that demonstrates the effect of a session lock. Just comment certain lines in or out to see the difference. I have forced a thread switch with Thread.Sleep(100) in various places to achieve the desired effects.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Wytek Szymanski
Web Developer
United States United States
I am a consultant, trainer, software archtect/engineer, since the early 1980s, working in the greater area of Boston, MA, USA.
 
My work comprises the entire spectrum of software, shrink-wrapped applications, IT client-server, systems and protocol related work, compilers and operating systems, and more ....
 
I am currently focused on platform development for distributed computing in service oriented data centers.

Comments and Discussions

 
QuestionHow can one contact Wytek Szymanski, the author of this arcticle? PinmemberYossiMimon2-May-08 0:07 
What is his email address?
 
Thank you!
Generalfile .zip Pinmemberluigi8122-Feb-04 8:15 
QuestionWhy not use this? Pinmembere_invalidarg30-Jan-04 2:25 
AnswerRe: Why not use this? PinmemberAndrew Phillips1-Feb-04 18:12 
GeneralMisleading. PinmemberWREY30-Jan-04 2:09 
GeneralRe: Misleading. PinmemberPyjamaSam2-Feb-04 4:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 30 Jan 2004
Article Copyright 2004 by Wytek Szymanski
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid