Wednesday, August 3, 2011

Java: Synchronized Threads Example

Here we create multiple threads that read and write to the variable "count". If the thread number is odd, it decrements count and if the thread number is even, it increments count.

The methods "countUP()" and "countDown()" have the modifier keyword "synchronized" commented out (/*synchronized*/) on lines 11 and 16 so any thread is allowed to access them at any time. This causes a problem, because when Java modifies the variable, it is not an atomic operation. While one thread is incrementing or decrementing count another thread could do the same before the previous thread can write the new value to memory. This would cause the value to be wrong.

If we remove the comment marks from the synchronized modifiers on lines 11 and 16, this prevents a thread from using these methods while another thread is using them. The current thread using the method must write the value back to memory and leave the method before another thread can access it.

If the methods are synchronized and we have an even number of threads running, the resulting value of count will be zero. Although this value can be zero when the program is run un-synchronized, there is no guarantee it will always be zero. If successive execution of the program is performed, one can see that the number will vary from zero.


UML Diagram


Click image for larger view.

//RunThreadsSync.java
 
import java.util.Random;

public class RunThreadsSync
{
  private static int args;
  private static int count;
  private ThreadX[] threadArray;

  public static /**synchronized*/ void countUp()
  {
   count++;
  }

  public static /**synchronized*/ void countDown()
  {
   count--;
  }

  public static int getCount()
  {
   return count;
  }

  public RunThreadsSync()
  {
   this.count = 0;
  }

  public static void main(String[] arguments)
  {
    long maxBytes = Runtime.getRuntime().maxMemory();
    System.out.println("Max memory: " + maxBytes / 1024 / 1024 + "M");

    if (arguments.length > 0)
    {
      try
      {
        args = Integer.parseInt(arguments[0]);
        if (args < 1 )
        {
          System.out.println("Argument must be greater than zero!");
          System.exit(1);
        }
        else if (args > 100)
        {
          System.out.println("Argument can't be greater than 100!");
          System.exit(1);
        }
      }
      catch(NumberFormatException e)
      {
        System.err.println("Argument must be an integer!");
        System.exit(1);
      }
    }
    else
    {
      System.out.println("No arguments given - defaulting to 4 threads.");
      args = 4;
    }
    System.out.println("Thread #main-" + Thread.currentThread());
    System.out.println(Thread.currentThread() + "-" + "Active!");
    RunThreadsSync rts = new RunThreadsSync();
    rts.makeThreads(args);
    System.out.println("count: " + getCount());
    System.out.println("Thread #main-" + Thread.currentThread() + "-Done!");
  }

  private void makeThreads (int noThreads)
  {
    threadArray = new ThreadX[noThreads];

    for(int i=0; i < noThreads; i++)
    {
      threadArray[i] = new ThreadX(i);
      threadArray[i].start();
    }

    for(int i=0; i < noThreads; i++)
    {
      try
      {
        threadArray[i].join();
      }
      catch (InterruptedException e)
      {
        //Ignore
      }
    }
  }

  private class ThreadX extends Thread
  {
    private int tnumber;
    private int rand;
    private Random randObj;

    private void setRand()
    {
      rand = randObj.nextInt(2) + 1;
    }

    ThreadX(int index)
    {
      this.tnumber = index;
      randObj  = new Random();
    }

    public void run()
    {
      System.out.println("Thread #" + tnumber + "-" + 
          Thread.currentThread());

      for(int i=0; i < 1000; i++)
      {
        setRand();

        try
        {
          Thread.sleep(rand);
        }
        catch(InterruptedException e)
        {
          //Ignore
        }

        if( i%100 == 0)
        {
          System.out.print(">");
        }

        if( tnumber%2 == 0 )
        {
          RunThreadsSync.countUp();
        }
        else
        {
          RunThreadsSync.countDown();
        }
      } 
      System.out.println("Thread #" + tnumber + "-" + Thread.currentThread() + 
          "-Done!");
      System.out.println("Thread #" + tnumber + "-" + "randObj hash code: " + 
          randObj.hashCode());
    } 
  }
}

RunThreadsSync Un-Synchronized Output

Click image for larger view.


RunThreadsSync Synchronized Output


Click image for larger view.


No comments:

Post a Comment