Header javaperspective.com
JavaPerspective.com  >   Intermediate Tutorials  >   2. Concurrency  >   2.4. Block synchronization

2.4. Block synchronization
Last updated: 31 January 2013.

This tutorial will show you how to synchronize a block of code in Java.

To synchronize a block of code, you must provide a monitor. A monitor is an object that controls access to a synchronized block. If a thread needs to execute a synchronized block, it must first acquire the monitor. After acquiring the monitor, the thread executes the statements within the synchronized block and releases the monitor. A thread cannot acquire a monitor that is already acquired by another thread. If a thread needs to acquire a monitor that is already acquired by another thread, it pauses execution until the monitor gets available. A synchronized block looks something like this:

synchronized(monitor){
   
// critical code section
    // ...
}

monitor is the object that controls access to the synchronized block. As an example, let's get back to the class Shared that you have seen in the previous tutorial. The method incrementArrayElements shown below has been slightly modified and now contains a synchronized block wherein the monitor is the current instance of the class Shared (keyword this):

public final class Shared {

   
private int[] array = new int[10];

   
public void incrementArrayElements() throws InterruptedException{

         
// non-critical statements may precede the synchronized block
          // ...

         
synchronized(this){
               
for(int i=0; i<array.length; ++i){
                     
array[i] = array[i] + 1;
                      Thread.sleep
(100);
               
}
          }

         
// non-critical statements may follow the synchronized block
          // ...
   
}

   
public int[] getArray(){
         
return array;
   
}

}

As you can see, I used the keyword this to specify that the monitor is the current instance of the class Shared. The class MyThread from the previous tutorial which uses the class Shared remains unchanged:

public final class MyThread extends Thread {

   
private Shared sharedInstance;

   
public MyThread(Shared sharedInstance){
         
this.sharedInstance = sharedInstance;
   
}

   
public void run(){
         
try{
               
sharedInstance.incrementArrayElements();
         
}
         
catch(InterruptedException e){

          }
    }

}

The class App containing the main method also remains unchanged:

public final class App {

   
public static void main(String[] args){
         
try{
               
Shared sharedInstance = new Shared();

                Thread thread1 =
new MyThread(sharedInstance);
                Thread thread2 =
new MyThread(sharedInstance);

                thread1.start
();
                thread2.start
();

                thread1.join
();
                thread2.join
();

               
for(int i : sharedInstance.getArray())
                     
System.out.println(i);
         
}
         
catch(InterruptedException e){

          }
    }

}

A monitor can be an object of any type. In the above example, I used the current instance of the class Shared. Nonetheless, for more flexibility, it is sometimes useful to create monitors explicitly. For example, let's assume that the class Shared contains another method named accessOtherData which also needs to synchronize critical code. However, because the methods accessOtherData and incrementArrayElements do not access the same data, when a thread is executing the critical code section in accessOtherData, another thread can execute at the same time the critical code section in incrementArrayElements. In that case, you can create two monitors as follows:

public final class Shared {

   
private int[] array = new int[10];

   
private Object monitor1 = new Object();
   
private Object monitor2 = new Object();


   
public void incrementArrayElements() throws InterruptedException{

         
// non-critical statements may precede the synchronized block
          // ...

         
synchronized(monitor1){
               
for(int i=0; i<array.length; ++i){
                     
array[i] = array[i] + 1;
                      Thread.sleep
(100);
               
}
          }

         
// non-critical statements may follow the synchronized block
          // ...
   
}


   
public void accessOtherData(){
         
synchronized(monitor2) {
               
// critical statements
                // ...
         
}
    }

   
public int[] getArray(){
         
return array;
   
}

}

Both blocks are synchronized. The synchronized block in the method incrementArrayElements cannot be executed simultaneously by multiple threads. Similarly, the synchronized block in the method accessOtherData cannot be executed by multiple threads at a time.

However, when a thread acquires monitor1 and is executing the corresponding block, another thread can acquire monitor2 and execute the corresponding block at the same time, which is not a problem since the two synchronized blocks do not access the same data. That would not have been possible with a unique monitor to control access to both blocks because in that case, a thread that acquires the monitor and enters the first block prevents other threads from entering the second block.

As you can see, explicitly created monitors offer greater flexibility. You can even use a monitor to synchronize blocks of code belonging to different classes. For example, you may need to have mutual exclusion between two blocks of code belonging to two different classes (class A and class B). When the block in class A is being executed by a thread, another thread must not enter the block in class B. To achieve that, all you have to do is create a unique monitor and synchronize both blocks on it.

An important point to note is that a synchronized method is nothing more than a synchronized block that uses the keyword this as a monitor. In other words, a synchronized method is a synchronized(this) block. In fact, if a class contains one or more synchronized methods, the current instance of that class is implicitly used as a monitor to control access to those synchronized methods.

Monitors are often called locks. If the current instance of a class is used as a monitor, that monitor is called an intrinsic lock. A thread is said to own the intrinsic lock of an object when it has acquired a monitor that is the object itself. For example, a thread that is executing a synchronized method on an object owns the intrinsic lock of that object. In the same way, a thread that is executing a synchronized(this) block for a given object owns the intrinsic lock of that object.

The synchronization features of the Java language must be used with extreme care to avoid issues like deadlock and starvation, especially if you are working on a large application that has a number of synchronized blocks and methods. A deadlock happens when several threads are waiting for each other forever in a situation where each thread is trying to acquire a monitor that will never be released. Starvation dramatically slows down a program when too many threads call a synchronized method that takes too long to return.


You are here :  JavaPerspective.com  >   Intermediate Tutorials  >   2. Concurrency  >   2.4. Block synchronization
Next tutorial :  JavaPerspective.com  >   Intermediate Tutorials  >   2. Concurrency  >   2.5. Wait and notify

Copyright © 2013. JavaPerspective.com. All rights reserved.  ( Terms | Contact | About ) 
Java is a trademark of Oracle Corporation
Image 1 Image 2 Image 3 Image 4 Image 5 Image 6 Image 7