Concurrent Programming with Java
Lab Manual, Version 1.0, F. Astha Ekadiyanto, 2002.

[Contents] [Next] [Previous]

Lab 5: Monitors and Thread Synchronization 1


Race Condition

In a multithreading environment, whenever there are two or more threads sharing access to a common object or memory, a race condition may occur. This is due to the way threads work (either concurrent or parallel) that may result in an inconsistent state. We will try a simple code to demonstrate this behaviour and try to overcome the problem.

The common Object that will be shared is the Briefkasten Class. The Class definition is very straight forward. It has a private field content, a get() method to get the content and a put() method to change the content.Create this class in your new BlueJ Project.

public class Briefkasten
{
   private int content;
   private boolean filled = false;
   public int get() 
   {
         int value = content;
         content = 0;
         filled = false;

         return value;

   }
   public void put(int value) 
   {
         filled = true;
         content = value;
   }
}

Then we will define two types of threads to share the Briefkasten, namely the Mieter Class that will access the get() method and the Postbote Class that will access the put() method.

The Mieter Class is as follows:

public class Mieter extends Thread 
{
     private Briefkasten briefkasten;
     private int roomnumber;       
     public Mieter(Briefkasten b, int number) 
     {
         briefkasten = b;
         roomnumber = number;
     }
     public void run() 
     {
         int value = 0;
         for (int i = 1; i < 11; i++) 
         {
            value = briefkasten.get();
            System.out.println("Mieter #" + roomnumber+ " got: " + value);
            try 
            {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { } } } }

The Postbote Class is as follows:

public class Postbote extends Thread 
{
    private Briefkasten briefkasten;
    private int postnumber;       
    public Postbote(Briefkasten b, int number) 
    {
         briefkasten = b;
         postnumber = number;
    }
    public void run() 
    {
         for (int i = 1; i < 11; i++) 
         {
             briefkasten.put(i);
             System.out.println("Postbote #" + postnumber+ " put: " + i);
             try 
             {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { } } } }

Now let us make a small application to test our codes. Create a RaceCondition Class as follows:

public class RaceCondition 
{
     public static void main(String[] args) 
     {  
         Briefkasten b = new Briefkasten();
         Mieter m1 = new Mieter(b, 1);
         Postbote b1 = new Postbote(b, 1);      

         b1.start();
         m1.start();

     }
}

Then run the application and observe the results. It will be more or less behaves like the followings:


Postbote #1 put: 1

Mieter #1 got: 1

Postbote #1 put: 2

Mieter #1 got: 2

Postbote #1 put: 3

Mieter #1 got: 3

Mieter #1 got: 0

Postbote #1 put: 4

Mieter #1 got: 4

Mieter #1 got: 0

Postbote #1 put: 5

Mieter #1 got: 5

Postbote #1 put: 6

Mieter #1 got: 6

Postbote #1 put: 7

Mieter #1 got: 7

Postbote #1 put: 8

Postbote #1 put: 9

Mieter #1 got: 9

We could run the main() method for a couple of times and results in different behavior but with similar symptom. The Postbote and the Mieter is racing to access the Briefkasten.

The problem with the codes is the state of the Briefkasten should be checked before changing the content. Let us try to address the problem by modifying the Briefkasten Class:

public class Briefkasten
{
   private int content;
   private boolean filled = false;
   public synchronized int get() 
   {
         if (filled == false) { 
              try {
wait();
} catch (InterruptedException e) { } }
int value = content; content = 0; filled = false; notifyAll(); return value; }
   public synchronized void put(int value) 
   {
         if (filled == true) { 
              try {
wait();
} catch (InterruptedException e) { } }
content = value; filled = true; notifyAll(); } }

Then run the application and observe the results. It will be more or less behaves like the followings:


Postbote #1 put: 1

Mieter #1 got: 1

Postbote #1 put: 2

Mieter #1 got: 2

Postbote #1 put: 3

Mieter #1 got: 3

Postbote #1 put: 4

Mieter #1 got: 4

Postbote #1 put: 5

Mieter #1 got: 5

Postbote #1 put: 6

Mieter #1 got: 6

Postbote #1 put: 7

Mieter #1 got: 7

Postbote #1 put: 8

Mieter #1 got: 8

Postbote #1 put: 9

Mieter #1 got: 9

Postbote #1 put: 10

Mieter #1 got: 10

Great! Now it works fine. The Mieter will only get some thing if it finds the Briefkasten is filled and the Postbote will put something only when it finds the Briefkasten is not filled otherwise, they will wait().

But! The code may have additional problem. The following modification in the RaceCondition Class will demonstrate the problem.

public class RaceCondition 
{
     public static void main(String[] args) 
     {  
         Briefkasten b = new Briefkasten();
         Mieter m1 = new Mieter(b, 1);
         Mieter m2 = new Mieter(b, 2);

         Postbote b1 = new Postbote(b, 1);      
         b1.start();

         m1.start();
         m2.start();

     }
}

Instead of only single Mieter, now we will have two Mieters to access the same Briefkasten.
Run the application and observe the results. It will be more or less behaves like the followings:

Postbote #1 put: 1

Mieter #1 got: 1

Postbote #1 put: 2

Mieter #2 got: 2

Mieter #1 got: 0

Postbote #1 put: 3

Mieter #2 got: 3

Postbote #1 put: 4

Mieter #1 got: 4

Postbote #1 put: 5

Mieter #2 got: 5

Postbote #1 put: 6

Mieter #1 got: 6

Postbote #1 put: 7

Mieter #2 got: 7

Postbote #1 put: 8

Mieter #2 got: 8

Postbote #1 put: 9

Mieter #1 got: 9

Mieter #2 got: 0

Postbote #1 put: 10

Mieter #1 got: 10

It seems like some Mieter still trying to access the Briefkasten when it is actually empty (indicated by 0 value). Could you solve the mystery?


[Contents] [Next] [Previous]