New York
University
Computer
Science Department
Courant
Institute of Mathematical Sciences
Session 5:
Thread safety
(Synchronization, immutable objects, and thread-safe wrappers)
Course Title: Extreme Java Course Number: g22.3033-007
Instructor: Jean-Claude Franchitti Session: 5
What is thread safety?
Thread safety simply means that the fields of an
object or class always maintain a valid state, as observed by other objects and
classes, even when used concurrently by multiple threads.
You should design classes such that objects maintain
a valid state, from the beginning of their lifetimes to the end. If you follow
this advice and create objects whose instance variables all are private and
whose methods only make proper state transitions on those instance variables,
you're in good shape in a single-threaded environment. But you may get into
trouble when more threads come along.
Multiple threads can spell trouble for your object
because often, while a method is in the process of executing, the state of your
object can be temporarily invalid. When just one thread is invoking the
object's methods, only one method at a time will ever be executing, and each
method will be allowed to finish before another method is invoked. Thus, in a
single-threaded environment, each method will be given a chance to make sure
that any temporarily invalid state is changed into a valid state before the
method returns.
Once you introduce multiple threads, however, the
JVM may interrupt the thread executing one method while the object's instance
variables are still in a temporarily invalid state. The JVM could then give a
different thread a chance to execute, and that thread could call a method on
the same object. All your hard work to make your instance variables private and
your methods perform only valid state transformations will not be enough to
prevent this second thread from observing the object in an invalid state.
Such an object would not be thread-safe, because in
a multithreaded environment, the object could become corrupted or be observed
to have an invalid state. A thread-safe object is one that always maintains a
valid state, as observed by other classes and objects, even in a multithreaded
environment.
Why worry about thread safety?
There are two big
reasons you need to think about thread safety when you design classes and
objects in Java:
1.
Support
for multiple threads is built into the Java language and API
2.
All
threads inside a Java virtual machine (JVM) share the same heap and method area
Because multithreading is built into Java, it is
possible that any class you design eventually may be used concurrently by
multiple threads. You needn't (and shouldn't) make every class you design
thread-safe, because thread safety doesn't come for free. But you should at
least think about thread safety every time you design a Java class.
You'll find a discussion of the costs of thread safety and guidelines
concerning when to make classes thread-safe later in this handout.
Given the architecture of the JVM, you need only be
concerned with instance and class variables when you worry about thread safety.
Because all threads share the same heap, and the heap is where all instance
variables are stored, multiple threads can attempt to use the same object's
instance variables concurrently. Likewise, because all threads share the same
method area, and the method area is where all class variables are stored,
multiple threads can attempt to use the same class variables concurrently. When
you do choose to make a class thread-safe, your goal is to guarantee the
integrity -- in a multithreaded environment -- of instance and class variables
declared in that class.
You needn't worry about multithreaded access to
local variables, method parameters, and return values, because these variables
reside on the Java stack. In the JVM, each thread is awarded its own Java
stack. No thread can see or use any local variables, return values, or
parameters belonging to another thread.
Given the structure of the JVM, local variables,
method parameters, and return values are inherently "thread-safe."
But instance variables and class variables will only be thread-safe if you
design your class appropriately.
RGBColor #1: Ready for a single thread
As an example of a
class that is not thread-safe, consider the RGBColor class, shown below.
Instances of this class represent a color stored in three private instance
variables: r, g, and b. Given the class shown below, an RGBColor object would begin its life
in a valid state and would experience only valid-state transitions, from the
beginning of its life to the end -- but only in a single-threaded environment.
// In
file threads/ex1/RGBColor.java
// Instances of this class are NOT 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);
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];
retVal[0] = r;
retVal[1] = g;
retVal[2] = b;
return retVal;
}
public 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();
}
}
}
Because the three instance variables, ints r, g, and b, are private, the only way other classes and
objects can access or influence the values of these variables is via RGBColor's constructor and methods.
The design of the constructor and methods guarantees that:
1.
RGBColor's constructor will always
give the variables proper initial values
2.
Methods
setColor() and invert() will always perform valid
state transformations on these variables
3.
Method
getColor() will always return a valid
view of these variables
Note that if bad data is passed to the constructor
or the setColor() method, they will complete
abruptly with an InvalidArgumentException. The checkRGBVals() method, which throws this
exception, in effect defines what it means for an RGBColor object to be valid: the
values of all three variables, r, g, and b, must be between 0 and 255,
inclusive. In addition, in order to be valid, the color represented by these
variables must be the most recent color either passed to the constructor or setColor() method, or produced by the invert() method.
If, in a single-threaded environment, you invoke setColor() and pass in blue, the RGBColor object will be blue when setColor() returns. If you then invoke
getColor() on the same object, you'll
get blue. In a single-threaded society, instances of this RGBColor class are well-behaved.
Throwing a concurrent wrench into the works
Unfortunately, this happy picture of a well-behaved RGBColor object can turn scary when
other threads enter the picture. In a multithreaded environment, instances of
the RGBColor class defined above are
susceptible to two kinds of bad behavior: write/write conflicts and read/write
conflicts.
Write/write conflicts
Imagine you have two threads, one thread named
"red" and another named "blue." Both threads are trying to
set the color of the same RGBColor object: The red thread is
trying to set the color to red; the blue thread is trying to set the color to
blue.
Both of these threads are trying to write to the
same object's instance variables concurrently. If the thread scheduler
interleaves these two threads in just the right way, the two threads will
inadvertently interfere with each other, yielding a write/write conflict. In
the process, the two threads will corrupt the object's state.
The Unsynchronized
RGBColor applet
The following table shows the sequence of events
demonstrated by an applet, named Unsynchronized RGBColor, that could
result in a corrupt RGBColor object. The red thread is
innocently trying to set the color to red while the blue thread is innocently
trying to set the color to blue. In the end, the RGBColor object represents neither
red nor blue but the unsettling color, magenta.
|
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 |
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 |
CheckRGBVals(255, 0, 0); |
0 |
0 |
0 |
|
|
red |
this.r = 255; |
0 |
0 |
0 |
|
|
red |
this.g = 0; |
255 |
0 |
0 |
|
|
red |
this.b = 0; |
255 |
0 |
0 |
|
|
red |
red thread returns |
255 |
0 |
0 |
|
|
blue |
later, blue thread continues |
255 |
0 |
0 |
|
|
blue |
this.b = 255 |
255 |
0 |
0 |
|
|
blue |
blue thread returns |
255 |
0 |
255 |
|
|
none |
object
represents magenta |
255 |
0 |
255 |
|
As you can see from this table, the RGBColor is corrupted because the
thread scheduler interrupts the blue thread while the object is still in a
temporarily invalid state. When the red thread comes in and paints the object
red, the blue thread is only partially finished painting the object blue. When
the blue thread returns to finish the job, it inadvertently corrupts the
object.
Read/write conflicts
Another kind of misbehavior that may be exhibited in
a multithreaded environment by instances of this RGBColor class is read/write
conflicts. This kind of conflict arises when an object's state is read and used
while in a temporarily invalid state due to the unfinished work of another
thread.
For example, note that during the blue thread's
execution of the setColor() method above, the object at
one point finds itself in the temporarily invalid state of black. Here, black
is a temporarily invalid state because:
1.
It
is temporary: Eventually, the blue thread intends to set the color to blue.
2.
It
is invalid: No one asked for a black RGBColor object. The blue thread is
supposed to turn a green object into blue.
If the blue thread is preempted at the moment the
object represents black by a thread that invokes getColor() on the same object, that
second thread would observe the RGBColor object's value to be black.
Here's a table that shows a sequence of events that
could lead to just such a read/write conflict:
|
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 |
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 getColor() |
0 |
0 |
0 |
|
|
red |
int[] retVal = new int[3]; |
0 |
0 |
0 |
|
|
Red |
retVal[0] = 0; |
0 |
0 |
0 |
|
|
Red |
retVal[1] = 0; |
0 |
0 |
0 |
|
|
Red |
retVal[2] = 0; |
0 |
0 |
0 |
|
|
Red |
return retVal; |
0 |
0 |
0 |
|
|
Red |
red thread returns black |
0 |
0 |
0 |
|
|
Blue |
later, blue thread continues |
0 |
0 |
0 |
|
|
Blue |
this.b = 255 |
0 |
0 |
0 |
|
|
blue |
blue thread returns |
0 |
0 |
255 |
|
|
none |
object
represents blue |
0 |
0 |
255 |
|
As you can see from this table, the trouble begins
when the blue thread is interrupted when it has only partially finished painting
the object blue. At this point the object is in a temporarily invalid state of
black, which is exactly what the red thread sees when it invokes getColor() on the object.
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 table shows a sequence of events
demonstrated by an applet, named Synchronized RGBColor. This sequence
of events is similar to the one that led to a corrupt RGBColor object in the previous
demonstration applet. This table, 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.
|
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.
Approach 2: Immutable objects
An alternative way to make an object thread-safe is
to make the object immutable. An immutable object is one whose state
can't be changed once the object is created.
Immutable objects are, by their very nature,
thread-safe simply because threads have to be able to write to an object's
instance variables to experience a read/write or write/write conflict. Because
no methods (only the constructor) of an immutable object actually write to the
object's instance variables, the object is by definition thread-safe.
In this approach to making an object thread-safe,
you don't mark critical sections as synchronized. Instead, you separate out the
critical sections that read instance variables from those that write to
instance variables. The critical sections that read are left as-is. The
critical sections that write must be changed so that, instead of altering the
current object's instance variables, they create a new object that embodies the
new state and returns a reference to that object.
RGBColor # 3: Thread safety through immutability
Here's
an immutable version of RGBColor:
// In
file threads/ex3/RGBColor.java
// Instances of this immutable class
// are thread-safe.
public class RGBColor {
private final int r;
private final int g;
private final int b;
public RGBColor(int r, int g, int b) {
checkRGBVals(r, g, b);
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];
retVal[0] = r;
retVal[1] = g;
retVal[2] = b;
return retVal;
}
public RGBColor invert() {
RGBColor retVal = new RGBColor(255 - r,
255 - g, 255 - b);
return retVal;
}
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();
}
}
}
Note that the setColor() method is simply removed,
as it doesn't make sense in an immutable RGBColor object. The getColor() method, which reads the
instance variables, is identical to what it has been, except now it doesn't
have to be synchronized. The invert() method, which writes to the instance variables, is
changed. Instead of inverting the current object's color, this new invert() creates a new RGBColor object that represents the
inverse of the object upon which invert() is invoked, and returns a
reference to that object.
Approach 3: Thread-safe wrappers
The third approach to making an object thread-safe
is to embed that object in a thread-safe wrapper object. In this approach you
leave the original class (which isn't thread-safe) unchanged and create a
separate class that is thread-safe. Instances of the new class serve as
thread-safe "front ends" to instances of the original class.
SafeRGBColor: A thread-safe wrapper
Here's an example of this approach applied to the very first version of RGBColor presented in this handout.
// In
file threads/ex1/SafeRGBColor.java
// Instances of this class are thread-safe
// wrappers of RGBColor objects, which are
// not thread-safe.
public class SafeRGBColor {
private RGBColor color;
public SafeRGBColor(int r, int g, int b) {
color = new RGBColor(r, g, b);
}
public synchronized void setColor(int r, int g, int b) {
color.setColor(r, g, b);
}
/**
* returns color in an array of three ints: R, G, and B
*/
public synchronized int[] getColor() {
return color.getColor();
}
public synchronized void invert() {
color.invert();
}
}
Why not just synchronize everything?
As mentioned earlier in this handout, you don't want
to make every class you design thread-safe -- only classes whose instances will
be used concurrently by multiple threads. The reason you don't want to make
every class thread-safe is that thread safety may involve a performance
penalty. For example:
·
Synchronized
method invocations generally are going to be slower than non-synchronized
method invocations. In Sun's current JVM, for example, synchronized method
invocations are 4 to 6 times slower than non-synchronized method invocations.
In the future, the speed of synchronized method invocations should improve, but
they will likely never achieve parity with non-synchronized method invocations.
·
Unnecessary
synchronized method invocations (and synchronized blocks) can cause unnecessary
blocking and unblocking of threads, which can hurt performance.
·
Immutable
objects tend to be instantiated more often, leading to greater numbers of often
short-lived objects that can increase the work of the garbage collector.
·
Synchronization
gives rise to the possibility of deadlock, a severe performance problem in
which your program appears to hang.
None
of these performance setbacks are good excuses for neglecting to make classes
that need to thread-safe so, but they do constitute good reasons not to make
classes thread-safe unnecessarily.
Pros and cons of the three approaches to thread safety
Synchronizing critical sections
Marking your code's critical sections as
synchronized is the "normal" approach to making classes synchronized.
It is also the only way to use wait() and notify() to get threads to cooperate towards achieving some
common goal. So the guideline concerning Approach 1 is simply:
Unless special circumstances make it appropriate to
use an immutable or wrapper object, use Approach 1 to make your class
thread-safe: Make sure the appropriate instance variables are private and mark
the critical sections as synchronized.
Using immutable objects
Achieving thread safety by making objects immutable
(Approach 2) works well when objects are small and represent values of a simple
abstract data type. The Java API includes several examples of immutable
objects, including String and the primitive type
wrappers such as Integer, Long, Float, Boolean, Character, and so on.
It's worth noting that instances of the AWT's Color class are immutable.
Likewise, the immutable approach may make sense for this handout's RGBColor class, which is similar in
functionality to the AWT's Color class, because RGBColor objects are small (they
contain only 3 ints) and conceptually
represent values of a simple abstract data type.
Another benefit of immutable objects is that you can
pass references to them to methods without worrying that the method will change
the object's state. In addition, if the overhead of immutability (excessive
creation of short-lived objects) may at times be too inefficient, you can also
define a mutable companion class that can be used when the immutable version
isn't appropriate. An example of this design approach in the Java API is the StringBuffer class, which serves as a
mutable companion to the immutable String class. Note that the StringBuffer class is also thread-safe,
but it uses the "normal" approach: its instance variables are private
and its critical sections are synchronized.
Using wrapper objects
The wrapper object approach to thread safety
(Approach 3) makes the most sense when you want to give clients a choice
between a version of a class that is thread-safe and one that isn't. This
approach also makes sense when you're a client of someone else's class that
isn't thread-safe, but you need to use the class in a multithreaded
environment. Once you define your own thread-safe wrapper for the class, you
can safely use the class in a multithreaded environment by going through your
wrapper.
A good example of this approach from the Java API
comes from the 1.2 collections library. The 1.2 collections library defines a
hierarchy that includes classes that represent many kinds of collections --
none of which are thread-safe. But class Collection includes several class
methods that will enclose a regular collection object in a thread-safe wrapper,
so you can safely use the object in a multithreaded context. This design gives
users of the collections library a choice of using a collections object that is
thread-safe and one that isn't.
Note that a common attribute of wrapper classes like
those you would use to add thread safety to the enclosed object is that the
wrapper accepts the same messages as the enclosed object. In other words, often
a wrapper class will descend from a common superclass or superinterface with
the enclosed class. (For those of you familiar with the Design Patterns
book by Gamma, et. al., this is the "decorator" pattern. See
"References" for more information on this book.) This decorator
design approach to wrappers, which is exhibited by the thread-safe wrappers of
the 1.2 collections library, allows the thread safety to be dynamically added
or removed from an object.
The advantage of the approach to wrapping taken by SafeRGBColor in this handout is that
thread safety is guaranteed when using a SafeRGBColor object, because the
enclosed RGBColor object is created by SafeRGBColor's constructor and never
returned by its own methods or passed to another object's methods. The
decorator design approach, because the enclosed object is instantiated by the
client and passed to the constructor of the thread-safe wrapper, requires that
clients create the enclosed objects themselves first. Thus, to achieve thread
safety, the decorator approach requires that clients have the discipline not to
use the enclosed object except through the thread-safe wrapper.
When to make classes thread-safe
When you are designing the classes that compose a
Java applet or application, your thread-safety decision should be based simply
on whether or not each class will be exposed to potential write/write or
read/write conflicts by your programs. To know whether or not such conflicts are
possible, you just have to know how your program will work.
If, instead of creating classes for an applet or
application, you are creating classes for a library, either one that will be
shared in-house or will serve as a product in its own right, you may not know
exactly how the classes will be used. In such cases, it may be a good idea to
give clients a choice via the thread-safe wrapper approach.
Conclusion
The most important point to take away from this
handout is that when programming in Java, you should at least think
about thread safety every time you design a class.
Here's a collection of the exception guidelines put
forth by this handout:
·
Given
that thread safety can have a performance cost, don't make every class
thread-safe -- only those classes that will actually be used concurrently by
multiple threads
·
Don't
avoid making classes thread-safe that need to be thread-safe out of fear of a
performance impact
·
When
making an object thread-safe via Approach 1, synchronize only the critical sections
of the class
·
Use
an immutable object especially if the object is small or represents a
fundamental data type
·
If
you can't change a non-thread-safe class, use a wrapper object
·
If
you are creating a library of classes that will be used in both thread-safe and
non-thread-safe requirements, consider making wrappers an option
References
·
Recommended
books on Java design
http://www.artima.com/designtechniques/booklist.html
·
Object
orientation FAQ
http://www.cyberdyne-object-sys.com/oofaq/
·
7237
Links on Object Orientation
http://www.rhein-neckar.de/~cetus/software.html
·
The
Object-Oriented Page
http://www.well.com/user/ritchie/oo.html
·
Collection
of information on OO approach
http://arkhp1.kek.jp:80/managers/computing/activities/OO_CollectInfor/OO_CollectInfo.html
·
Design
Patterns Home Page
http://hillside.net/patterns/patterns.html
·
A
Comparison of OOA and OOD Methods
http://www.iconcomp.com/papers/comp/comp_1.html
·
Object-Oriented
Analysis and Design Methods: A Comparative Review
http://wwwis.cs.utwente.nl:8080/dmrg/OODOC/oodoc/oo.html
·
Patterns
discussion FAQ
http://gee.cs.oswego.edu/dl/pd-FAQ/pd-FAQ.html
·
Implementing
Basic Design Patterns in Java (Doug Lea)
http://www.oswego.edu/dl/pats/ifc.html
·
Patterns
in Java AWT
http://mordor.cs.hut.fi/tik-76.278/group6/awtpat.html
·
Software
Technology's Design Patterns Page
http://www.sw-technologies.com/dpattern/