How to Add Buttons and Text to Your JavaFX Project
How to Import Sample Programs for Java Programming
Try Statements in Java

How to Synchronize Methods When Using Java Threads

Whenever you work on a Java program that uses threads, you have to consider the nasty issue of concurrency. In particular, what if two threads try to access a method of an object at precisely the same time? Unless you program carefully, the result can be disastrous. A method that performs a simple calculation returns inaccurate results.

In an online banking application, you might discover that some deposits are credited twice and some withdrawals aren’t credited at all. In an online ordering system, one customer’s order might get recorded in a different customer’s account.

The key to handling concurrency issues is recognizing methods that update data and that might be called by more than one thread. After you identify those methods, the solution is simple. You just add the synchronized keyword to the method declaration, like this:

public synchronized void someMethod()...

This code tells Java to place a lock on the object so that no other methods can call any other synchronized methods for the object until this method finishes. In other words, it temporarily disables multithreading for the object.

Here are some concrete examples. This code creates an instance of the CountDownClock class.

import java.util.concurrent.ScheduledThreadPoolExecutor;
public class DoTwoThings
{
 ScheduledThreadPoolExecutor pool =
  new ScheduledThreadPoolExecutor(2);
 CountDownClock clock = new CountDownClock(20);
 public static void main(String[] args)
 {
  new DoTwoThings();
 }
 DoTwoThings()
 {
  pool.execute(clock);
  pool.execute(clock);
  pool.shutdown();
 }
}

The resulting output is an unpredictable mishmash of two threads’ outputs, with some of the counts duplicated and others skipped altogether, like this:

T minus 20
T minus 20
T minus 19
T minus 19
T minus 18
T minus 17
T minus 16
T minus 15
T minus 13
T minus 13
T minus 12
T minus 12
T minus 11
T minus 11
T minus 10
T minus 9
T minus 7
T minus 7
T minus 6
T minus 5
T minus 4
T minus 3
T minus 2
T minus 2
T minus 1
T minus 0

The two threads execute their loops simultaneously, so after one thread displays its T minus 20, the other thread displays its own T minus 20. The same thing happens for T minus 19, T minus 18, and so on.

Then this code spawns two threads, each of which runs a copy of the CountDownClock instance's code.

import java.util.concurrent.ScheduledThreadPoolExecutor;
public class DoTwoThingsSync
{
 ScheduledThreadPoolExecutor pool =
  new ScheduledThreadPoolExecutor(2);
 CountDownClockSync clock =
  new CountDownClockSync(20);
 public static void main(String[] args)
 {
  new DoTwoThingsSync();
 }
 DoTwoThingsSync()
 {
  pool.execute(clock);
  pool.execute(clock);
  pool.shutdown();
 }
}

Java's synchronized keyword ensures that only one thread at a time calls the run method. The resulting output shows one complete execution of the run method followed by another.

class CountDownClockSync extends Thread
{
 private int start;
 public CountDownClockSync(int start)
 {
  this.start = start;
 }
 synchronized public void run()
 {
  for (int t = start; t >= 0; t--)
  {
   System.out.println("T minus " + t);
   try
   {
    Thread.sleep(1000);
   }
   catch (InterruptedException e)
   {}
  }
 }
}

The two threads’ calls to the run method are not interleaved, so the output counts down from 20 to 0 and then counts down a second time from 20 to 0:

T minus 20
T minus 19
T minus 18

And so on, down to

T minus 2
T minus 1
T minus 0
T minus 20
T minus 19
T minus 18

And so on, down to

T minus 2
T minus 1
T minus 0

The tough part is knowing which methods to synchronize. Any method that updates instance variables is at risk — and needs to be synchronized. That’s because when two or more threads run a method at the same time, the threads have a common copy of the method’s instance variables.

Even methods that consist of just one line of code are at risk. Consider this method:

int sequenceNumber = 0;
public int getNextSequenceNumber()
{
 return sequenceNumber++;
}

You’d think that because this method has just one statement, some other thread could not interrupt it in the middle. Alas, that’s not the case. This method must get the value of the sequenceNumber field, add 1 to it, save the updated value back to the sequenceNumber field, and return the value.

In fact, this single Java statement compiles to 11 bytecode instructions. If the thread is preempted between any of those bytecodes by another thread calling the same method, the serial numbers get munged.

For safety’s sake, why not just make all the methods synchronized? You have two reasons not to do so:

  • Synchronizing methods takes time. Java has to acquire a lock on the object being synchronized, run the method, and then release the lock. But before it can do that, it has to check to make sure that some other thread doesn’t already have a lock on the object. All this work takes time.

  • More important, synchronizing all your methods defeats the purpose of multithreading, so you should synchronize only those methods that require it.

The synchronized keyword doesn’t block all access to an object. Other threads can still run unsynchronized methods of the object while the object is locked.

The Object class provides three methods that can let synchronized objects coordinate their activities. The wait method puts a thread in the waiting state until some other thread calls either the object’s notify or (more commonly) notifyAll method. These methods are useful when one thread has to wait for another thread to do something before it can proceed.

The classic example is a banking system in which one thread makes withdrawals and the other makes deposits. If a customer’s account balance drops to zero, the thread that makes withdrawals can call wait; then the thread that makes deposits can call notifyAll. That way, each time a deposit is made, the withdrawal thread can recheck the customer’s account balance to see whether it now has enough money.

  • Add a Comment
  • Print
  • Share
blog comments powered by Disqus
Create a Generic Class in Java
Use Comments to Experiment with Java Code
Catching Errors in Java
Select Which Version of Java to Use with Eclipse
Keep Things Simple with Java Classes
Advertisement

Inside Dummies.com