一、前言

要理解ThreadLocal,首先必须理解线程安全。线程可以看做是一个具有一定独立功能的处理过程,它是比进程更细度的单位。当程序以单线程运行的时候,我们不需要考虑线程安全。然而当一个进程中包含多个线程的时候,就需要考虑线程安全问题,因为此时线程可能会同时操作同一个资源,当两个或者两个以上线程同时操作一个资源的时候,就会造成冲突、不一致等问题,即线程不安全。

解决线程安全问题,本质上就是解决资源共享问题,一般有以下手段:

1)可重入(不依赖环境);2)互斥(同一时间段只允许一个线程使用);3)原子操作;4)Thread-Local

二、Thread-Local

Thread-Local是一个很简单的思想:如果一个资源会引起线程竞争,那就为每一个线程配备一个资源。这就是ThreadLocal需要做的事情。

三、ThreadLocal的用法

在理解ThreadLocal之前,首先看一下它的用法:

 public class ThreadLocalTest {
public static ThreadLocal<Integer> intLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
// TODO Auto-generated method stub
return 0;
} @Override
public Integer get() {
// TODO Auto-generated method stub
set(super.get() + 1);
return super.get();
} @Override
public void set(Integer value) {
// TODO Auto-generated method stub
super.set(value);
} @Override
public void remove() {
// TODO Auto-generated method stub
super.remove();
}
}; public static void main(String[] args) {
// TODO Auto-generated method stub
for(int index = 0; index < 3; index ++)
new MyThread(index).start();
}
} class MyThread extends Thread{
int id; public MyThread(int id){
this.id = id;
} @Override
public void run() {
// TODO Auto-generated method stub
for(int index = 0; index < 3; index ++){
System.out.println("Thread-" + id + " : " + ThreadLocalTest.intLocal.get());
try {
Thread.sleep((int)(100 * Math.random()));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

其打印结果如下:

 Thread-1 : 1
Thread-0 : 1
Thread-2 : 1
Thread-0 : 2
Thread-2 : 2
Thread-1 : 2
Thread-2 : 3
Thread-0 : 3
Thread-1 : 3

这是一个很典型的问题,在学习多线程以及同步的时候,几乎所有的书本都会使用类似的一个例子:银行存钱取钱问题。我们知道当多线程并发操作一个int值的加减操作的时候,最后的数值会产生很大的不确定性,得不到最终正确的结果。

而从例子中我们可以看到:每一个线程对int值的操作都是独立的,我们使用的只是同一个静态的intLocal类型!通过使用TreadLocal,我们可以为每一个线程提供独立的资源副本,从而完成对资源的“共享”操作。

ThreadLocal类中可重载的方法只有四个:

1)set():设置值,也就是说,我们选择将某个值设置为ThreadLocal类型的;

2)get():将设置进去的值取出来;

3)remove():我们不想将某个值设置为ThreadLocal了,移除掉;

4)initialValue():如果get的时候还没有设置值,就使用这个方法进行初始化;

使用过程简单明了,一般重载initialValue()提供一个初始值就可以了,其余方法不需要重载。

四、ThreadLocal的实现

看源码是最直接也是最有效的学习方式,不但可以掌握其原理,也可以学习Java源码精巧的实现方式。

ThreadLocal的实现代码略长,我们选取重要的方法作为切入点:

     public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}

第一个方法是get()方法。这边出现了Thread和Map,联想到Thread的作用,大致应该可以猜到ThreadLocal的实现:维护一个Map,以Thread作为键,以变量作为值,每一次要使用变量的时候,就以当前的Thread作为键去取值,如果没有,就初始化一个值返回,如果有,就直接返回。

事实上,ThreadLocal的实现思路的确大致如此。但是我们要做的事情其实更多:

A.如果要设置更多的值怎么办?也就是说,我们有多种资源需要共享怎么办?

     B.为每一个线程共享一个资源,如何回收?

我们言归正传,从源码中获取答案。

get()方法的第3行中出现了一个ThreadLocalMap实例,它是从getMap()方法获取的,其方法如下:

 ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

t表示当前的线程,从Thread的源码中可以看到,的确是有一个ThreadLocalMap实例,其声明和注解如下:

 /* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

这个属性是专门为ThreadLocal类而存在的,而它的实现也存在于ThreadLocal中,是ThreadLocal的一个静态内部类。其类注释如下:

 /**
* 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.
*/

  它是一个定制的HashMap(自然具有HashMap的相关特性,比如自动扩增容量等)。

  前面提到A,B两个问题,从源码来看,A问题的解决方案就是,为每一个Thread维护一个HashMap,在这里就是维护一个ThreadLocalMap属性,这个属性的键是ThreadLocal,值就是资源副本,详细描述如下:

  每一个Thread都有一个ThreadLocalMap属性,这个属性是类似于HashMap的,它以ThreadLocal为键,以属于该线程的资源副本为值。我们可以这样看待ThreadLocal:ThreadLocal是为一组线程维护资源副本的对象,通过它,可以为每一个线程创建资源副本,也可以正确获得属于某一线程的资源副本。

  每一个ThreadLocal只能维护一个共享资源,一旦声明ThreadLocal实例,线程在调用的其get()方法获取资源副本的时候,就可以自动设置绑定到该线程本身。

  好,现在转了一小圈回到get方法()。get()方法的第2、3行很明显是获取属于当前线程的ThreadLocalMap,如果这个map不为空,我们就以当前的ThreadLocal为键,去获取相应的Entry,Entry是ThreadLocalMap的静态内部类,其定义如下:

 static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}

  它继承与弱引用,所以在get()方法里面如第7行一样调用e.value方法就可以获取实际的资源副本值。但是如果有一个为空,说明属于该线程的资源副本还不存在,则需要去创建资源副本,从代码中可以看到是调用setInitialValue()方法,其定义如下:

 /**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

  第8行调用initialValue()方法初始化一个值,还记得在一开始的例子中,我们重载这个方法产生一个初始化的值么?

  接下来是判断线程的ThreadLocalMap是否为空,不为空就直接这是值(键为this,值为value),为空则创建一个Map,调用方法为createMap(),其定义如下:

 void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

  简单明了,而ThreadLocalMap的这个构造方法的实现如下:

/**
* 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.
*/
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}

  实例化table数组用于存储键值对,然后通过映射将键值对存储进入相应的位置。

  至于set方法,看完get()后应该很简单了,自己都可以实现:

 /**
* 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 {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

  现在还剩一个问题B,会造成内存泄露吗?ThreadLocal的类注释里面有一段话:

/* Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the <tt>ThreadLocal</tt>
* 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).
*/

  每一个线程对资源副本都有一个隐式引用:只要线程还在运行,只要ThreadLocal还是可以获取的。当一个线程运行结束销毁时,所有的资源副本都是可以被垃圾回收的。这段注释表明,ThreadLocal的使用是不会造成内训泄露的。

  但是我们来仔细分析一下,我想画一张Thread、ThreadLocal和ThreadLocalMap的依赖关系图,但是在绘制过程中,我发现:根本就没有依赖!

  非常惊讶,我在这里把ThreadLocal完整的源码贴一遍,读者可以自行审视。

 package java.lang;
import java.lang.ref.*;
import java.util.concurrent.atomic.AtomicInteger; /**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized
* copy of the variable. <tt>ThreadLocal</tt> instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes <tt>ThreadId.get()</tt>
* and remains unchanged on subsequent calls.
* <pre>
* 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&lt;Integer> threadId =
* new ThreadLocal&lt;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();
* }
* }
* </pre>
* <p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the <tt>ThreadLocal</tt>
* 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 Josh Bloch and Doug Lea
* @since 1.2
*/
public class ThreadLocal<T> {
/**
* 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.
*/
private final int threadLocalHashCode = nextHashCode(); /**
* The next hash code to be given out. Updated atomically. Starts at
* zero.
*/
private static AtomicInteger nextHashCode =
new AtomicInteger(); /**
* 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.
*/
private static final int HASH_INCREMENT = 0x61c88647; /**
* Returns the next hash code.
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
} /**
* 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 {@link #get}
* method, unless the thread previously invoked the {@link #set}
* method, in which case the <tt>initialValue</tt> 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 {@link #remove} followed by {@link #get}.
*
* <p>This implementation simply returns <tt>null</tt>; if the
* programmer desires thread-local variables to have an initial
* value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
* subclassed, and this method overridden. Typically, an
* anonymous inner class will be used.
*
* @return the initial value for this thread-local
*/
protected T initialValue() {
return null;
} /**
* Creates a thread local variable.
*/
public ThreadLocal() {
} /**
* 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 {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
} /**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return 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 {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
} /**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* <tt>initialValue</tt> method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
} /**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
} /**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
* @param map the map to store.
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
} /**
* Factory method to create map of inherited thread locals.
* Designed to be called only from Thread constructor.
*
* @param parentMap the map associated with parent thread
* @return a map containing the parent's inheritable bindings
*/
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
} /**
* 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.
*/
T childValue(T parentValue) {
throw new UnsupportedOperationException();
} /**
* 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.
*/
static class ThreadLocalMap { /**
* 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.
*/
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value; Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
} /**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16; /**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table; /**
* The number of entries in the table.
*/
private int size = 0; /**
* The next size value at which to resize.
*/
private int threshold; // Default to 0 /**
* Set the resize threshold to maintain at worst a 2/3 load factor.
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
} /**
* Increment i modulo len.
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
} /**
* Decrement i modulo len.
*/
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.
*/
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
} /**
* Construct a new map including all Inheritable ThreadLocals
* from given parent map. Called only by createInheritedMap.
*
* @param parentMap the map associated with parent thread.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len]; for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
ThreadLocal key = e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
} /**
* 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.
*
* @param key the thread local object
* @return the entry associated with key, or null if no such
*/
private Entry getEntry(ThreadLocal key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
} /**
* Version of getEntry method for use when key is not found in
* its direct hash slot.
*
* @param key the thread local object
* @param i the table index for key's hash code
* @param e the entry at table[i]
* @return the entry associated with key, or null if no such
*/
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length; while (e != null) {
ThreadLocal k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
} /**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal key, Object value) { // We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not. Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get(); if (k == key) {
e.value = value;
return;
} if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
} tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
} /**
* Remove the entry for key.
*/
private void remove(ThreadLocal key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
} /**
* 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.)
*
* @param key the key
* @param value the value to be associated with key
* @param staleSlot index of the first stale entry encountered while
* searching for key.
*/
private void replaceStaleEntry(ThreadLocal key, Object value,
int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e; // Back up to check for prior stale entry in current run.
// We clean out whole runs at a time to avoid continual
// incremental rehashing due to garbage collector freeing
// up refs in bunches (i.e., whenever the collector runs).
int slotToExpunge = staleSlot;
for (int i = prevIndex(staleSlot, len);
(e = tab[i]) != null;
i = prevIndex(i, len))
if (e.get() == null)
slotToExpunge = i; // Find either the key or trailing null slot of run, whichever
// occurs first
for (int i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal k = e.get(); // If we find key, then we need to swap it
// with the stale entry to maintain hash table order.
// The newly stale slot, or any other stale slot
// encountered above it, can then be sent to expungeStaleEntry
// to remove or rehash all of the other entries in run.
if (k == key) {
e.value = value; tab[i] = tab[staleSlot];
tab[staleSlot] = e; // Start expunge at preceding stale entry if it exists
if (slotToExpunge == staleSlot)
slotToExpunge = i;
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
} // If we didn't find stale entry on backward scan, the
// first stale entry seen while scanning for key is the
// first still present in the run.
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
} // If key not found, put new entry in stale slot
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value); // If there are any other stale entries in run, expunge them
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
} /**
* 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
*
* @param staleSlot index of slot known to have null key
* @return the index of the next null slot after staleSlot
* (all between staleSlot and this slot will have been checked
* for expunging).
*/
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length; // expunge entry at staleSlot
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--; // Rehash until we encounter null
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null; // Unlike Knuth 6.4 Algorithm R, we must scan until
// null because multiple entries could have been stale.
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
} /**
* 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.
*
* @param i a position known NOT to hold a stale entry. The
* scan starts at the element after i.
*
* @param n scan control: <tt>log2(n)</tt> cells are scanned,
* unless a stale entry is found, in which case
* <tt>log2(table.length)-1</tt> 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.)
*
* @return true if any stale entries have been removed.
*/
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len;
removed = true;
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
} /**
* 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.
*/
private void rehash() {
expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis
if (size >= threshold - threshold / 4)
resize();
} /**
* Double the capacity of the table.
*/
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0; for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null; // Help the GC
} else {
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
} setThreshold(newLen);
size = count;
table = newTab;
} /**
* Expunge all stale entries in the table.
*/
private void expungeStaleEntries() {
Entry[] tab = table;
int len = tab.length;
for (int j = 0; j < len; j++) {
Entry e = tab[j];
if (e != null && e.get() == null)
expungeStaleEntry(j);
}
}
}
}

  ThreadLocal对Thread的引用全部通过局部变量完成,而没有一个全局变量。而实际的资源副本则存储在Thread的自身的属性ThreadLocalMap中,这说明,其实ThreadLocal只是关联一个Thread和其资源副本的桥梁,并且实际上Thread和资源副本的生命周期是紧密相连的,的的确确如ThreadLocal所说,在线程被回收的时候,其资源副本也会被回收,虽然ThreadLocal是静态的,但是它既不引用Thread,也不引用ThreadLocalMap。真是精巧的设计!

五、ThreadLocal作用

其实ThreadLocal所实现的功能前面已经描述的很清楚了,但是从网上的讨论来看,大家对ThreadLocal所要达成的目的意见却不是很统一,比如博客《彻底理解ThreadLocal》,具体可以看一下类的注释:

/**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized
* copy of the variable. <tt>ThreadLocal</tt> instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*/

从这个注解来看,和TLS(可以参见《Thread Local Storage》)大致是一样的,其实不用去深究其具体目的。个人觉得,ThreadLocal的功能就是为每一个线程提供一个资源副本,当你有这样的需求的时候,就可以使用ThreadLocal去解决问题。

最新文章

  1. Sql Servicer 复习笔记(1) 存储过程分布
  2. 【转】Xamarin.Android 入门之:Xamarin+vs2015 环境搭建
  3. 【Todo】蒙特卡洛(蒙特卡罗)树 &amp; 卷积网络
  4. LintCode &quot;4 Sum&quot;
  5. CISCO3560 VLAN配置实例
  6. JavaServer Faces 2.2 requires Dynamic Web Module 2.5 or newer
  7. Yum本地Rpm库设置
  8. xadmin学习笔记(二)——改造Django教程实例(1)
  9. Adapter 适配器模式
  10. 【后缀数组】【poj2774】【 Long Long Message】
  11. EJBCA于Linux安装在
  12. jquery入门知识点总结(转)
  13. 请求方式:request和 get、post、put
  14. java编程(1)——servlet和Ajax异步请求的接口编程(没有调用数据库的数据)
  15. ABP 2.0.2 升到 2.2.1
  16. CMake在Visual Studio下保持目录结构
  17. sqlyog一些快捷键
  18. saltstack自动化运维系列②之saltstack的数据系统
  19. JPA实体类中常用的注解
  20. Mysql在Linux的基本操作文档

热门文章

  1. 基本hibernate DEMO
  2. 在Html中使用Requirejs进行模块化开发
  3. Linux asyn-io for socket
  4. Atitit.常用的gc算法
  5. Leetcode 110 Balanced Binary Tree 二叉树
  6. ios之VFL的补充(三)
  7. VS2010几款超赞的扩展辅助工具总结
  8. php中抓取网页内容的代码
  9. 连接UI到代码
  10. Git教程学习(五)