The Artima Developer Community
Sponsored Link

Designing for Thread Safety
Using Synchronization, Immutable Objects, and Thread-Safe Wrappers
by Bill Venners
First Published in JavaWorld, July 1998

<<  Page 5 of 11  >>

Advertisement

Three ways to make an object thread-safe
There are basically three approaches you can take to make an object such as RGBThread thread-safe:

  1. Synchronize critical sections
  2. Make it immutable
  3. Use a thread-safe wrapper

Approach 1: Synchronizing the critical sections
The most straightforward way to correct the unruly behavior exhibited by objects such as RGBColor 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.

To take this approach to making your object thread-safe, you must follow two steps: you must make all relevant fields private, and you must identify and synchronize all the critical sections.

Step 1: Make fields private
Synchronization means that only one thread at a time will be able to execute a bit of code (a critical section). So even though it's fields you want to coordinate access to among multiple threads, Java's mechanism to do so actually coordinates access to code. This means that only if you make the data private will you be able to control access to that data by controlling access to the code that manipulates the data.

The first rule to follow when making a class thread-safe through synchronizing its critical sections, therefore, is to make its fields private. 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.

Step 2: Identify and synchronize critical sections
Once you've made the appropriate variables private, you need only mark the object's critical sections as synchronized. As mentioned above, a critical sectionis a bit of code that must be executed atomically, that is, as a single, indivisible operation. For example, the statements:

this.r = r;
this.g = g;
this.b = b;

must operate atomically for the setColor() method to behave as expected in a multithreaded environment. To ensure proper behavior, these three statements need to appear as if they were executed by a single, indivisible JVM instruction.

Note that reads and writes of primitive types and object references are atomic by definition, except for longs and doubles. 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 longs and doubles. 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 longs and doubles, therefore, should always be synchronized.

RGBColor # 2: Thread safety through synchronization
Here's a revised version of the RGBColor() class. This version, which has its critical sections marked as synchronized, is thread-safe:

// In file threads/ex2/RGBColor.java
// Instances of this class are thread-safe.
public class RGBColor {

    private int r;
    private int g;
    private int b;

    public RGBColor(int r, int g, int b) {

        checkRGBVals(r, g, b);

        this.r = r;
        this.g = g;
        this.b = b;
    }

    public void setColor(int r, int g, int b) {

        checkRGBVals(r, g, b);

        synchronized (this) {

            this.r = r;
            this.g = g;
            this.b = b;
        }
    }

    /**
    * returns color in an array of three ints: R, G, and B
    */
    public int[] getColor() {

        int[] retVal = new int[3];

        synchronized (this) {

            retVal[0] = r;
            retVal[1] = g;
            retVal[2] = b;
        }

        return retVal;
    }

    public synchronized void invert() {

        r = 255 - r;
        g = 255 - g;
        b = 255 - b;
    }

    private static void checkRGBVals(int r, int g, int b) {

        if (r < 0 || r > 255 || g < 0 || g > 255 ||
            b < 0 || b > 255) {

            throw new IllegalArgumentException();
        }
    }
}

The Synchronized RGBColor applet
The following applet, named Synchronized RGBColor, demonstrates a similar sequence of events to the one that led to a corrupt RGBColor object in the previous demonstration applet. This applet, however, shows how the thread-safe version of RGBColor is able to maintain a valid state, even when multiple threads are attempting to write to the object. As before, a red thread is trying to set the color to red while a blue thread is trying to set the color to blue. In the end, this RGBColor object represents not the invalid color magenta, but the valid -- and satisfying -- color red.

For some reason, your browser won't let you see this way-cool Java applet.

To step through the sequence of events that led to a corrupted RGBColor object, press the applet's Step button. Press Back to back up a step, and Reset to back up to the beginning. As you go, a line of text at the bottom of the applet will explain what's happening during each step.

For those of you who can't run the applet, here's a table that shows the sequence of events demonstrated by the applet:

Thread Statement r g b Color
none object represents green 0 255 0  
blue blue thread invokes setColor(0, 0, 255) 0 255 0  
blue blue thread acquires lock 0 255 0  
blue checkRGBVals(0, 0, 255); 0 255 0  
blue this.r = 0; 0 255 0  
blue this.g = 0; 0 255 0  
blue blue gets preempted 0 0 0  
red red thread invokes setColor(255, 0, 0) 0 0 0  
red red thread blocks because object locked 0 0 0  
blue later, blue thread continues 0 0 0  
blue this.b = 255 0 0 0  
blue blue thread returns and releases lock 0 0 255  
red later, red thread acquires lock and continues 0 0 255  
red checkRGBVals(255, 0, 0); 0 0 255  
red this.r = 255; 0 0 255  
red this.g = 0; 255 0 255  
red this.b = 0; 255 0 255  
red red thread returns and releases lock 255 0 0  
none object represents red 255 0 0  

Note that this version of RGBColor still has temporarily invalid states from time to time. To be specific, at times during the sequence shown above this object's state does represent the invalid states black and magenta. The trick to synchronization is that while an object is having one of those temporarily invalid moments, no other classes or objects are allowed to use or observe the state of the object via other threads.

<<  Page 5 of 11  >>


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2017 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us