站内搜索: 请输入搜索关键词
当前页面: 图书首页 > Java Threads, Third Edition

5.3 Thread Local Variables - Java Threads, Third Edition

Previous Section  < Day Day Up >  Next Section

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.

    Previous Section  < Day Day Up >  Next Section