Thread-safe access to shared data

Imagine if you could lock a shared object while it was being accessed by a thread; you’d be able to prevent other threads from modifying it. This is exactly what Java does to make its data thread safe. Making your applications thread safe means securing your shared data so that it stores correct data, even when it’s accessed by multiple threads. Thread safety isn’t about safe threads—it’s about safeguarding your shared data that might be accessible to multiple threads. A thread-safe class stores correct data without requiring calling classes to guard it.

You can lock objects by defining synchronized methods and synchronized statements. Java implements synchronization by using monitors, covered in the next quick warm- up section.

OBJECT LOCKS AND MONITORS: WARM-UP :

Every Java object is associated with a monitor, which can be locked or unlocked by a thread. At a time, only one thread can hold lock on a monitor and is referred to as the monitor owner. Owning the monitor is also referred to as acquiring the monitor. If another thread wants to acquire the monitor of that object, it must wait for it to be released.

When the keyword synchronized is added to a method, only one thread can execute it at a time. To execute a synchronized method, a thread must acquire the monitor of the object on which the method is called. When the monitor of an object is locked, no other thread can execute any synchronized method on that object. When acquired, a thread can release the lock on the monitor if

  • It has completed its execution.

  • It needs to wait for another operation to complete.

For the first case, it releases the lock and exits. For the latter case, it enters a waiting set of threads that are waiting to acquire the monitor again. Threads enter the waiting set due to an execution of wait. They can reacquire the monitor when notify- All() or notify() is called.

Figure below shows how a thread might have to wait to hold a lock on a monitor. Only one thread can own the monitor of an object, and a thread might release the monitor and enter a waiting state.

A thread must own a monitor before it can execute synchronized code. Only the monitor owner can execute the synchronized code.

With an understanding of how objects and their monitors, threads, and execution of synchronized code work together, let’s modify the code used in section Thread interference so that threads working with shared data don’t interleave.

SYNCHRONIZED METHODS

Synchronized methods are defined by prefixing the definition of a method with the key- word synchronized. You can define both instance and static methods as synchronized methods. The methods, which modify the state of instance or static variables, should be defined as synchronized methods. This prevents multiple threads from modifying the shared data in a manner that leads to incorrect values.

When a thread invokes a synchronized method, it automatically locks the monitor. If the method is an instance method, the thread locks the monitor associated with the instance on which it’s invoked (referred to as this within the method). For static methods, the thread locks the monitor associated with the Class object, thereby representing the class in which the method is defined. These locks are released once execution of the synchronized method completes, with or without an exception.

For nonstatic instance synchronized methods, a thread locks the monitor of the object on which the synchronized method is called. To execute static synchronized methods, a thread locks the monitor associated with the Class object of its class.

In the following listing, let’s modify the definition of class Book by defining its meth- ods newSale() and returnBook() as synchronized methods. The methods that belong to the data being protected are defined as synchronized.

Listing 1:

Figure below shows how threads task2, task1, and task3 might acquire a lock on the monitor of object book defined in the main method of class ShoppingCart. As you see, a thread can’t execute synchronized methods newSale() and returnBook() on book without acquiring a lock on its monitor. So each thread has exclusive access to book to modify its state, resulting in a correct book state at the completion of main.

A thread releases the lock on an object monitor after it exits a synchronized method, whether due to successful completion or due to an exception.

CODE SNIPPET:

1)

package com.gs.corejava.collections;

class Book {

	String title;
	int copiesSold = 0;

	Book(String title) {
		this.title = title;
	}

	public synchronized void newSale() {
		int reg = copiesSold;
		reg++;
		copiesSold = reg;
	}

	public synchronized void returnBook() {
		int reg = copiesSold;
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		reg--;
		copiesSold = reg;
	}

}

class OnlineBuy extends Thread {
	Book book;

	public OnlineBuy(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.newSale();
	}

}

class OnlineReturn extends Thread {
	Book book;

	public OnlineReturn(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.returnBook();
	}

}

public class ShoppingCart {
	public static void main(String[] args) {
		Book book = new Book("Java");
		Thread onlineBuy1 = new OnlineBuy(book);
		Thread onlineBuy2 = new OnlineBuy(book);
		Thread onlineReturn = new OnlineReturn(book);

		onlineReturn.start();
		onlineBuy1.start();
		onlineBuy2.start();

		try {
			onlineBuy1.join();
			onlineBuy2.join();
			onlineReturn.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(book.copiesSold);

	}

}
1 // ALWAYS 1

What happens if, instead of defining methods newSale() and returnBook() as synchronized methods (see listing 1), you define the run() methods in the threads OnlineBuy and OnlineReturn as synchronized methods? Will this modification protect the data of shared object book in ShoppingCart? Let’s answer this question in the next “Twist in the Tale” exercise. Take a closer look; apart from the mentioned modification, I’ve also modified some other bits of code.

Twist In Tale :

Purpose: To synchronize the right methods to protect your shared data

Answer: c

Explanation: Because the methods newSale() and returnBook() aren’t defined as synchronized methods, multiple threads can access these methods concurrently, posing a risk of thread interference that fails to protect the data of object book, defined on line 3. Defining the run() methods as synchronized doesn’t help to protect the data of object book. It restricts execution of run to a single thread; it doesn’t restrict modification of an instance of Book to a single thread. To protect your shared data, you should add the synchronized keyword to the methods that directly manipulate your shared data.

As stated before, when a thread acquires a lock on the object monitor, no other thread can execute any other synchronized method on the object until the lock is released. This could become inefficient if your class defined synchronized methods that manipulate different sets of unrelated data. To do so, you might mark a block of statements with the keyword synchronized, as covered in the next section.

SYNCHRONIZED STATEMENTS:

To execute synchronized statements, a thread must acquire a lock on an object moni- tor. For an instance method, it might not acquire a lock on the instance itself. You can specify any object on which a thread must acquire the monitor lock before it can exe- cute the synchronized statements.

In the previous example for class Book, let’s remove the keyword synchronized from the definition of method newSale() and define synchronized statements in it:

Multiple threads can concurrently execute methods with synchronized statements if they acquire locks on monitors of separate objects.

A thread releases the lock on the object monitor once it exits the synchronized statement block due to successful completion or an exception. If you’re trying to modify your shared data using synchronized statements, ensure that the data items are mutually exclusive. As shown in the preceding example, the object references objSale and objPos refer to different objects.

CODE SNIPPET :

1) Snippet of above example:

package com.gs.corejava.threads;

class Book {
	double rank = 0;
	int copiesSold = 0;

	Object objSale = new Object();
	Object objPos = new Object();

	public void newSale() {
		synchronized (objSale) {
			++copiesSold;
		}
	}

	public void rankImprove() {
		synchronized (objPos) {
			--rank;
		}
	}
}

class OnlineSale implements Runnable {
	private Book book;

	public OnlineSale(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.newSale();
	}

}

class OnlineRankBooster implements Runnable {

	private Book book;

	public OnlineRankBooster(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.rankImprove();
	}

}

public class BookApp {
	public static void main(String[] args) {
		Book book = new Book();
		Thread onlineSaleTask1 = new Thread(new OnlineSale(book));
		Thread onlineSaleTask2 = new Thread(new OnlineSale(book));
		Thread onlineRankBooster1 = new Thread(new OnlineRankBooster(book));

		onlineSaleTask1.start();
		onlineSaleTask2.start();
		onlineRankBooster1.start();
	}

}

2) Giving names and printing states of threads

package com.gs.corejava.threads;

import java.util.Set;

class Book {
	double rank = 0;
	int copiesSold = 0;

	Object objSale = new Object();
	Object objPos = new Object();

	public void newSale() {
		synchronized (objSale) {
			++copiesSold;
		}
	}

	public void rankImprove() {
		synchronized (objPos) {
			--rank;
		}
	}
}

class OnlineSale implements Runnable {
	private Book book;

	public OnlineSale(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.newSale();
	}

}

class OnlineRankBooster implements Runnable {

	private Book book;

	public OnlineRankBooster(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.rankImprove();
	}

}

public class BookApp {
	public static void main(String[] args) {
		Book book = new Book();
		Thread onlineSaleTask1 = new Thread(new OnlineSale(book));
		Thread onlineSaleTask2 = new Thread(new OnlineSale(book));
		Thread onlineRankBooster1 = new Thread(new OnlineRankBooster(book));

		onlineSaleTask1.setName("onlineSaleTask1");
		onlineSaleTask1.start();
		onlineSaleTask2.setName("onlineSaleTask2");
		onlineSaleTask2.start();
		onlineRankBooster1.setName("onlineRankBooster1");
		onlineRankBooster1.start();

		Set<Thread> threads = Thread.getAllStackTraces().keySet();
		for (Thread thread : threads) {
			System.out.println(thread.getName() + " -- " + thread.getState());
		}
	}

}
Reference Handler -- WAITING
onlineSaleTask2 -- RUNNABLE
Finalizer -- WAITING
onlineRankBooster1 -- RUNNABLE
Signal Dispatcher -- RUNNABLE
main -- RUNNABLE
onlineSaleTask1 -- BLOCKED
Attach Listener -- RUNNABLE

3)

package com.gs.corejava.threads;

import java.util.Set;

class Book {
	double rank = 0;
	int copiesSold = 0;


	public void newSale() {
		synchronized (this) {
			++copiesSold;
		}
	}

	public void rankImprove() {
		synchronized (this) {
			--rank;
		}
	}
}

class OnlineSale implements Runnable {
	private Book book;

	public OnlineSale(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.newSale();
	}

}

class OnlineRankBooster implements Runnable {

	private Book book;

	public OnlineRankBooster(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.rankImprove();
	}

}

public class BookApp {
	public static void main(String[] args) {
		Book book = new Book();
		Thread onlineSaleTask1 = new Thread(new OnlineSale(book));
		Thread onlineSaleTask2 = new Thread(new OnlineSale(book));
		Thread onlineRankBooster1 = new Thread(new OnlineRankBooster(book));

		onlineSaleTask1.setName("onlineSaleTask1");
		onlineSaleTask1.start();
		onlineSaleTask2.setName("onlineSaleTask2");
		onlineSaleTask2.start();
		onlineRankBooster1.setName("onlineRankBooster1");
		onlineRankBooster1.start();

		Set<Thread> threads = Thread.getAllStackTraces().keySet();
		for (Thread thread : threads) {
			System.out.println(thread.getName() + " -- " + thread.getState());
		}
	}

}

The Java synchronized Keyword

Synchronized blocks in Java are marked with the synchronized keyword. A synchronized block in Java is synchronized on some object. All synchronized blocks synchronized on the same object can only have one thread executing inside them at the same time. All other threads attempting to enter the synchronized block are blocked until the thread inside the synchronized block exits the block.

The synchronized keyword can be used to mark four different types of blocks:

  1. Instance methods

  2. Static methods

  3. Code blocks inside instance methods

  4. Code blocks inside static methods

These blocks are synchronized on different objects. Which type of synchronized block you need depends on the concrete situation.

Synchronized Instance Methods

Here is a synchronized instance method:

  public synchronized void add(int value){
      this.count += value;
  }

Notice the use of the synchronized keyword in the method declaration. This tells Java that the method is synchronized.

A synchronized instance method in Java is synchronized on the instance (object) owning the method. Thus, each instance has its synchronized methods synchronized on a different object: the owning instance. Only one thread can execute inside a synchronized instance method. If more than one instance exist, then one thread at a time can execute inside a synchronized instance method per instance. One thread per instance.

Synchronized Static Methods

Static methods are marked as synchronized just like instance methods using the synchronized keyword. Here is a Java synchronized static method example:

  public static synchronized void add(int value){
      count += value;
  }

Also here the synchronized keyword tells Java that the method is synchronized.

Synchronized static methods are synchronized on the class object of the class the synchronized static method belongs to. Since only one class object exists in the Java VM per class, only one thread can execute inside a static synchronized method in the same class.

If the static synchronized methods are located in different classes, then one thread can execute inside the static synchronized methods of each class. One thread per class regardless of which static synchronized method it calls.

Synchronized Blocks in Instance Methods

You do not have to synchronize a whole method. Sometimes it is preferable to synchronize only part of a method. Java synchronized blocks inside methods makes this possible.

Here is a synchronized block of Java code inside an unsynchronized Java method:

  public void add(int value){

    synchronized(this){
       this.count += value;   
    }
  }

This example uses the Java synchronized block construct to mark a block of code as synchronized. This code will now execute as if it was a synchronized method.

Notice how the Java synchronized block construct takes an object in parentheses. In the example "this" is used, which is the instance the add method is called on. The object taken in the parentheses by the synchronized construct is called a monitor object. The code is said to be synchronized on the monitor object. A synchronized instance method uses the object it belongs to as monitor object.

Only one thread can execute inside a Java code block synchronized on the same monitor object.

The following two examples are both synchronized on the instance they are called on. They are therefore equivalent with respect to synchronization:

 
  public class MyClass {
  
    public synchronized void log1(String msg1, String msg2){
       log.writeln(msg1);
       log.writeln(msg2);
    }

  
    public void log2(String msg1, String msg2){
       synchronized(this){
          log.writeln(msg1);
          log.writeln(msg2);
       }
    }
  }

Thus only a single thread can execute inside either of the two synchronized blocks in this example.

Had the second synchronized block been synchronized on a different object than this, then one thread at a time had been able to execute inside each method.

Synchronized Blocks in Static Methods

Here are the same two examples as static methods. These methods are synchronized on the class object of the class the methods belong to:

  public class MyClass {

    public static synchronized void log1(String msg1, String msg2){
       log.writeln(msg1);
       log.writeln(msg2);
    }

  
    public static void log2(String msg1, String msg2){
       synchronized(MyClass.class){
          log.writeln(msg1);
          log.writeln(msg2);  
       }
    }
  }

Only one thread can execute inside any of these two methods at the same time.

Had the second synchronized block been synchronized on a different object than MyClass.class, then one thread could execute inside each method at the same time.

Code Snippet:

package com.gs.corejava.collections;

class Book {

	String title;
	static int copiesSold = 0;

	Book(String title) {
		this.title = title;
	}

	public static synchronized void newSale() {
		int reg = copiesSold;
		reg++;
		copiesSold = reg;
	}

	public static synchronized void returnBook() {
		int reg = copiesSold;
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		reg--;
		copiesSold = reg;
	}

}

class OnlineBuy extends Thread {
	Book book;

	public OnlineBuy(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.newSale();
	}

}

class OnlineReturn extends Thread {
	Book book;

	public OnlineReturn(Book book) {
		this.book = book;
	}

	@Override
	public void run() {
		book.returnBook();
	}

}

public class ShoppingCart {
	public static void main(String[] args) {
		Book book = new Book("Java");
		Thread onlineBuy1 = new OnlineBuy(book);
		Thread onlineBuy2 = new OnlineBuy(book);
		Thread onlineReturn = new OnlineReturn(book);

		onlineReturn.start();
		onlineBuy1.start();
		onlineBuy2.start();
		
		Book book1 = new Book("SQL");
		Thread onlineBuy11 = new OnlineBuy(book1);
		Thread onlineBuy22 = new OnlineBuy(book1);
		Thread onlineReturn1 = new OnlineReturn(book1);

		onlineReturn1.start();
		onlineBuy11.start();
		onlineBuy22.start();


		try {
			onlineBuy1.join();
			onlineBuy2.join();
			onlineReturn.join();
			onlineBuy11.join();
			onlineBuy22.join();
			onlineReturn1.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(book.copiesSold);
		System.out.println(book1.copiesSold);

	}

}

Java Synchronized Example

Here is an example that starts 2 threads and have both of them call the add method on the same instance of Counter. Only one thread at a time will be able to call the add method on the same instance, because the method is synchronized on the instance it belongs to.

  public class Counter{
     
     long count = 0;
    
     public synchronized void add(long value){
       this.count += value;
     }
  }
  public class CounterThread extends Thread{

     protected Counter counter = null;

     public CounterThread(Counter counter){
        this.counter = counter;
     }

     public void run() {
	for(int i=0; i<10; i++){
           counter.add(i);
        }
     }
  }
  public class Example {

    public static void main(String[] args){
      Counter counter = new Counter();
      Thread  threadA = new CounterThread(counter);
      Thread  threadB = new CounterThread(counter);

      threadA.start();
      threadB.start(); 
    }
  }

Two threads are created. The same Counter instance is passed to both of them in their constructor. The Counter.add() method is synchronized on the instance, because the add method is an instance method, and marked as synchronized. Therefore only one of the threads can call the add() method at a time. The other thread will wait until the first thread leaves the add() method, before it can execute the method itself.

If the two threads had referenced two separate Counter instances, there would have been no problems calling the add() methods simultaneously. The calls would have been to different objects, so the methods called would also be synchronized on different objects (the object owning the method). Therefore the calls would not block. Here is how that could look:

  public class Example {

    public static void main(String[] args){
      Counter counterA = new Counter();
      Counter counterB = new Counter();
      Thread  threadA = new CounterThread(counterA);
      Thread  threadB = new CounterThread(counterB);

      threadA.start();
      threadB.start(); 
    }
  }

Notice how the two threads, threadA and threadB, no longer reference the same counter instance. The add method of counterA and counterB are synchronized on their two owning instances. Calling add() oncounterA will thus not block a call to add() on counterB.

Last updated