The most straightforward way to correct the unruly behavior exhibited by non thread safe objects when placed in a multithreaded context is to synchronize the object's critical sections.
An object's
critical sections are those methods or blocks of code within methods that must be executed by only one thread at a time. Put another way, a critical section is a method or block of code that must be executed atomically, as a single, indivisible operation. By using Java's
synchronized
keyword, you can guarantee that only one thread at a time will ever execute the object's critical sections.
This approach takes two steps,
to make all relevant fields private, and
to identify and synchronize all the critical sections.
Any field that you need to coordinate multithreaded access to must be private, otherwise it may be possible for other classes and objects to ignore your critical sections and access the fields directly. Not every field must be private - only those that will be involved in any temporarily invalid states created by the object's or class's critical sections. For example, constants (static final variables) can't be corrupted by multiple threads, so they needn't be private.
A
critical section is a bit of code that must be executed atomically, that is, as a single, indivisible operation.
Note that reads and writes of primitive types and object references are atomic by definition, except for long
s and double
s. This means that if you have an int
, for example, that is independent of any other fields in an object, you needn't synchronize code that accesses that field. If two threads were to attempt to write two different values to the int
concurrently, the resulting value would be one or the other. The int
would never end up with a corrupted value made up of some bits written by one thread and other bits written by the other thread.
The same is not necessarily true, however, for long
s and double
s. If two different threads were to attempt to write two different values to a long
concurrently, you might just end up with a corrupted value consisting of some bits written by one thread and other bits written by the other thread. Multithreaded access to long
s and double
s, therefore, should always be synchronized.
For example, a Price class has two instances variables, double priceValue and Date effectiveDate. And the class has a updatePrice method,
public void updatePrice(doubel priceValue, effectiveDate) {
this.priceValue = priceValue;
this.effectiveDate = effectiveDate;
}
The way to make Price thread safe is to make the priceValue and the effectiveDate private first,
then synchronize the updatePrice method.
public
synchronized void updatePrice(double priceValue, effectiveDate) {
...
}
or
public void ... {
synchronized (this) { this.priceValue = priceValue;
this.effectiveDate = effectiveDate;
}}