THE Java™ Programming Language, Fourth Edition

(Jeff_L) #1

The synchronized statement in the constructor acquires the lock of the Class object for Body in the
same way a synchronized static method of the class would. Synchronizing on this would use a different
object for each invocation and so would not prevent threads from accessing nextID concurrently. It would
also be wrong to use the Object method getClass to retrieve the Class object for the current instance:
In an extended class, such as AttributedBody, that would return the Class object for
AttributedBody not Body, and so again, different locks would be used and interference would not be
prevented. The simplest rule is to always protect access to static data using the lock of the Class object for
the class in which the static data was declared.


In many cases, instead of using a synchronized statement you can factor the code to be protected into a
synchronized method of its own. You'll need to use your own experience and judgment to decide when
this is preferable. For example, in the Body class we could encapsulate access to nextID in a static
synchronized method getNextID.


Finally, the ability to acquire locks of arbitrary objects with synchronized statements makes it possible to
perform the client-side synchronization that you saw in the array example. This capability is important, not
only for protecting access to objects that don't have synchronized methods, but also for synchronizing a series
of accesses to an object. We look more at this in the next section.


14.3.4. Synchronization Designs


Designing the appropriate synchronization for a class can be a complex matter and it is beyond the scope of
this book to delve too deeply into these design issues. We can take a brief look at some of the issues involved.


Client-side synchronization is done by having all the clients of a shared object use synchronized
statements to lock the object before using it. Such a protocol is fragileit relies on all of the clients doing the
right thing. It is generally better to have shared objects protect access to themselves by making their methods
synchronized (or using appropriate synchronized statements inside those methods). This makes it
impossible for a client to use the object in a way that is not synchronized. This approach is sometimes termed
server-side synchronization, but it is just an extension of the object-oriented perspective that objects
encapsulate their own behavior, in this case synchronization.


Sometimes a designer hasn't considered a multithreaded environment when designing a class, and none of the
class's methods are synchronized. To use such a class in a multithreaded environment, you have to decide
whether to use client-side synchronization via synchronized statements or to create an extended class to
override the appropriate methods, declare them synchronized, and forward method calls through the
super reference.


If you are working with an interface instead of a class, you can provide an alternate implementation that wraps
the methods of the interface in synchronized methods that forward the calls to another unsynchronized object
that implements the same interface. This will work with any implementation of the interface, and is therefore
a better solution than extending each class to use super in a synchronized method. This flexibility is another
reason to use interfaces to design your system. You can see an example of this synchronized wrapper
technique in the collections classes; see "The Synchronized Wrappers" on page 602.


The synchronization you have learned about so far is the simplest notion of "thread safety"the idea that
methods on objects can be invoked by multiple threads concurrently and each thread will have the method
perform its expected job. Synchronization, however, has to extend to more complex situations involving
multiple method invocations on an object, or even method invocations on multiple objects. If these series of
invocations must appear as an atomic action you will need synchronization. In the case of multiple method
invocations, you can encapsulate the series of invocations within another method and synchronize that
method, but generally this is impracticalyou can't define all combinations of the basic methods as methods
themselves, nor are you likely to know at the time the class is designed which method combinations may be

Free download pdf