5.3 Thread Local Variables
Any thread can, at any time, define a thread local variable that is
private to that particular thread. Other threads that define the same
variable create their own copy of the variable. This means that
thread local variables cannot be used to share state between threads;
changes to the variable in one thread are private to that thread and
not reflected in the copies held by other threads. But it also means
that access to the variable need never be synchronized since
it's impossible for multiple threads to access the
variable. Thread local variables have other uses, of course, but
their most common use is to allow multiple threads to cache their own
data rather than contend for synchronization locks around shared
data.
A thread local variable is modeled by
the java.lang.ThreadLocal
class:
public class ThreadLocal<T> {
protected T initialValue( );
public T get( );
public void set(T value);
public void remove( );
}
In typical usage, you subclass the
ThreadLocal class and override the
initialValue()
method to return the value that should be
returned the first time a thread accesses the variable. The subclass
rarely needs to override the other methods of the
ThreadLocal class; instead, those methods are used
as a
getter/setter
pattern for the thread-specific value.
One case where you might use a thread local variable to avoid
synchronization is in a thread-specific
cache. Consider the
following class:
package javathreads.examples.ch05.example4;
import java.util.*;
public abstract class Calculator {
private static ThreadLocal<HashMap> results = new ThreadLocal<HashMap>( ) {
protected HashMap initialValue( ) {
return new HashMap( );
}
};
public Object calculate(Object param) {
HashMap hm = results.get( );
Object o = hm.get(param);
if (o != null)
return o;
o = doLocalCalculate(param);
hm.put(param, o);
return o;
}
protected abstract Object doLocalCalculate(Object param);
}
Thread local objects are declared static so that the object itself
(that is, the results variable in this example) is
shared among all threads. When the get() method
of the thread local variable is called, the internal mechanism of the
thread local class returns the specific object assigned to the
specific thread. The initial value of that object is returned from
the initialValue() method of the class extending
ThreadLocal; when you create a thread local
variable, you are responsible for implementing that method to return
the appropriate (thread-specific) object.
When the calculate() method in our example is
called, the thread local hash map is consulted to see if the value
has previously been calculated. If so, that value is returned;
otherwise, the calculation is performed and the new value stored in
the hash map. Since access to the map is from only a single thread,
we're able to use a HashMap
object rather than a Hashtable object (or
otherwise synchronizing the hash map).
This approach is worthwhile only if the calculation is very expensive
since obtaining the hash map itself requires synchronizing on all the
threads. If the reference returned from the thread-local
get() method is held a long time, it may be worth
exploring this type of design since otherwise that reference would
need to be synchronized for a long time. Otherwise,
you're just trading one synchronization call for
another. And in general, the performance of the
ThreadLocal class has been fairly dismal, though
this situation improved in JDK 1.4 and even more in J2SE 5.0.
Another case where this technique is useful is dealing with
thread-unsafe classes. If each thread instantiates the necessary
object in a thread local variable, it has its own copy that it can
safely access.
5.3.1 Inheritable Thread Local Variables
Values stored by threads in thread local variables are unrelated.
When a new thread is created, it gets a new copy of the thread local
variable, and the value of that variable is what's
returned by the initialValue() method of the
thread local subclass.
An alternative to this idea is the
InheritableThreadLocal class:
package java.lang;
public class InheritableThreadLocal extends ThreadLocal {
protected Object childValue(Object parentValue);
}
This class allows a child thread to inherit the value of the thread
local variable from its parent; that is, when the get() method of the thread local variable is called by the
child thread, it returns the same value as when that method is called
by the parent thread.
If you like, you can use the childValue()
method to further augment this behavior.
When the child thread calls the get() method of
the thread local variable, the get() method looks
up the value associated with the parent thread. It then passes that
value to the childValue() method and returns that
result. By default, the childValue() method
simply returns its argument, so no transformation occurs.
|