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.

OBJECT LOCKS AND MONITORS:

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 yield or 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.

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 asso- ciated with the Class object of its class.

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.

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.

Problem 1 :

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 monitor. 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.

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 state- ment 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 mutu- ally exclusive. As shown in the preceding example, the object references objSale and objPos refer to different objects.

Last updated