This class provides thread-local variables. These variables differ from
their normal counterparts in that each thread that accesses one (via its
get or
set method) has its own, independently initialized
copy of the variable.
ThreadLocal instances are typically private
static fields in classes that wish to associate state with a thread (e.g.,
a user ID or Transaction ID).
For example, the class below generates unique identifiers local to each
thread.
A thread's id is assigned the first time it invokes ThreadId.get()
and remains unchanged on subsequent calls.
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
Each thread holds an implicit reference to its copy of a thread-local
variable as long as the thread is alive and the ThreadLocal
instance is accessible; after a thread goes away, all of its copies of
thread-local instances are subject to garbage collection (unless other
references to these copies exist).
- Author(s):
- Josh Bloch and Doug Lea
- Since:
- 1.2
ThreadLocals rely on per-thread linear-probe hash maps attached
to each thread (Thread.threadLocals and
inheritableThreadLocals). The ThreadLocal objects act as keys,
searched via threadLocalHashCode. This is a custom hash code
(useful only within ThreadLocalMaps) that eliminates collisions
in the common case where consecutively constructed ThreadLocals
are used by the same threads, while remaining well-behaved in
less common cases.
The next hash code to be given out. Updated atomically. Starts at
zero.
The difference between successively generated hash codes - turns
implicit sequential thread-local IDs into near-optimally spread
multiplicative hash values for power-of-two-sized tables.
Returns the next hash code.
Returns the current thread's "initial value" for this
thread-local variable. This method will be invoked the first
time a thread accesses the variable with the
get()
method, unless the thread previously invoked the
set(java.lang.Object)
method, in which case the
initialValue method will not
be invoked for the thread. Normally, this method is invoked at
most once per thread, but it may be invoked again in case of
subsequent invocations of
remove() followed by
get().
This implementation simply returns null; if the
programmer desires thread-local variables to have an initial
value other than null, ThreadLocal must be
subclassed, and this method overridden. Typically, an
anonymous inner class will be used.
- Returns:
- the initial value for this thread-local
Creates a thread local variable.
Returns the value in the current thread's copy of this
thread-local variable. If the variable has no value for the
current thread, it is first initialized to the value returned
by an invocation of the
initialValue() method.
- Returns:
- the current thread's value of this thread-local
Variant of set() to establish initialValue. Used instead
of set() in case user has overridden the set() method.
- Returns:
- the initial value
Sets the current thread's copy of this thread-local variable
to the specified value. Most subclasses will have no need to
override this method, relying solely on the
initialValue()
method to set the values of thread-locals.
- Parameters:
value the value to be stored in the current thread's copy of
this thread-local.
public void set(T value) { Removes the current thread's value for this thread-local
variable. If this thread-local variable is subsequently
read by the current thread, its value will be
reinitialized by invoking its
initialValue() method,
unless its value is set by the current thread
in the interim. This may result in multiple invocations of the
initialValue method in the current thread.
Get the map associated with a ThreadLocal. Overridden in
InheritableThreadLocal.
- Parameters:
t the current thread- Returns:
- the map
Create the map associated with a ThreadLocal. Overridden in
InheritableThreadLocal.
- Parameters:
t the current threadfirstValue value for the initial entry of the mapmap the map to store.
Factory method to create map of inherited thread locals.
Designed to be called only from Thread constructor.
- Parameters:
parentMap the map associated with parent thread- Returns:
- a map containing the parent's inheritable bindings
Method childValue is visibly defined in subclass
InheritableThreadLocal, but is internally defined here for the
sake of providing createInheritedMap factory method without
needing to subclass the map class in InheritableThreadLocal.
This technique is preferable to the alternative of embedding
instanceof tests in methods.
ThreadLocalMap is a customized hash map suitable only for
maintaining thread local values. No operations are exported
outside of the ThreadLocal class. The class is package private to
allow declaration of fields in class Thread. To help deal with
very large and long-lived usages, the hash table entries use
WeakReferences for keys. However, since reference queues are not
used, stale entries are guaranteed to be removed only when
the table starts running out of space.
The entries in this hash map extend WeakReference, using
its main ref field as the key (which is always a
ThreadLocal object). Note that null keys (i.e. entry.get()
== null) mean that the key is no longer referenced, so the
entry can be expunged from table. Such entries are referred to
as "stale entries" in the code that follows.
The value associated with this ThreadLocal.
The initial capacity -- MUST be a power of two.
The table, resized as necessary.
table.length MUST always be a power of two.
The number of entries in the table.
The next size value at which to resize.
Set the resize threshold to maintain at worst a 2/3 load factor.
private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0);
private static int prevIndex(int i, int len) { return ((i - 1 >= 0) ? i - 1 : len - 1);
Construct a new map initially containing (firstKey, firstValue).
ThreadLocalMaps are constructed lazily, so we only create
one when we have at least one entry to put in it.
Construct a new map including all Inheritable ThreadLocals
from given parent map. Called only by createInheritedMap.
- Parameters:
parentMap the map associated with parent thread.
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
for (int j = 0; j < len; j++) { Entry e = parentTable[j];
int h = key.threadLocalHashCode & (len - 1);
Get the entry associated with key. This method
itself handles only the fast path: a direct hit of existing
key. It otherwise relays to getEntryAfterMiss. This is
designed to maximize performance for direct hits, in part
by making this method readily inlinable.
- Parameters:
key the thread local object- Returns:
- the entry associated with key, or null if no such
if (e != null && e.get() == key)
Version of getEntry method for use when key is not found in
its direct hash slot.
- Parameters:
key the thread local objecti the table index for key's hash codee the entry at table[i]- Returns:
- the entry associated with key, or null if no such
Set the value associated with key.
- Parameters:
key the thread local objectvalue the value to be set
int i = key.threadLocalHashCode & (len-1);
tab[i] = new Entry(key, value);
Remove the entry for key.
int i = key.threadLocalHashCode & (len-1);
Replace a stale entry encountered during a set operation
with an entry for the specified key. The value passed in
the value parameter is stored in the entry, whether or not
an entry already exists for the specified key.
As a side effect, this method expunges all stale entries in the
"run" containing the stale entry. (A run is a sequence of entries
between two null slots.)
- Parameters:
key the keyvalue the value to be associated with keystaleSlot index of the first stale entry encountered while
searching for key.
int slotToExpunge = staleSlot;
if (slotToExpunge == staleSlot)
if (k == null && slotToExpunge == staleSlot)
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
if (slotToExpunge != staleSlot)
Expunge a stale entry by rehashing any possibly colliding entries
lying between staleSlot and the next null slot. This also expunges
any other stale entries encountered before the trailing null. See
Knuth, Section 6.4
- Parameters:
staleSlot index of slot known to have null key- Returns:
- the index of the next null slot after staleSlot
(all between staleSlot and this slot will have been checked
for expunging).
tab[staleSlot].value = null;
int h = k.threadLocalHashCode & (len - 1);
Heuristically scan some cells looking for stale entries.
This is invoked when either a new element is added, or
another stale one has been expunged. It performs a
logarithmic number of scans, as a balance between no
scanning (fast but retains garbage) and a number of scans
proportional to number of elements, that would find all
garbage but would cause some insertions to take O(n) time.
- Parameters:
i a position known NOT to hold a stale entry. The
scan starts at the element after i.n scan control: log2(n) cells are scanned,
unless a stale entry is found, in which case
log2(table.length)-1 additional cells are scanned.
When called from insertions, this parameter is the number
of elements, but when from replaceStaleEntry, it is the
table length. (Note: all this could be changed to be either
more or less aggressive by weighting n instead of just
using straight log n. But this version is simple, fast, and
seems to work well.)- Returns:
- true if any stale entries have been removed.
if (e != null && e.get() == null) { } while ( (n >>>= 1) != 0);
Re-pack and/or re-size the table. First scan the entire
table removing stale entries. If this doesn't sufficiently
shrink the size of the table, double the table size.
Double the capacity of the table.
int oldLen = oldTab.length;
Entry[] newTab = new Entry[newLen];
for (int j = 0; j < oldLen; ++j) { int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
Expunge all stale entries in the table.
for (int j = 0; j < len; j++) { if (e != null && e.get() == null)