java中的ThreadLocal,顧名思義就是線程本地信息。官網給出的定義以下:java
This class provides thread-local variables.These variables differ from
their normal counterparts in that each thread that accesses one (via its
{@code get} or {@code set} method) has its own, independently initialized
copy of the variable. {@code 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).
複製代碼
ThreadLocal的方法安全
public T get()
public void set(T value)
public void remove()
複製代碼
以set方法爲例,看下具體實現bash
/**
* 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);
}
複製代碼
這裏又出現一個ThreadLocalMap類,經過getMap方法獲得,參數爲當前線程tide
/**
* 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;
}
複製代碼
這個getMap方法獲取的就是當前線程的局部變量threadLocals,其定義以下:ui
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
複製代碼
因此ThreadLocal類的get/set方法,其實都是首先獲取當前線程t,而後根據線程t獲得線程的ThreadLocalMap變量,接下來就是對這個線程變量進行操做,這裏才明白什麼叫作線程局部變量。this
/**
* 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.
*/
複製代碼
其實ThreadLocalMap就是一個自定義實現的HashMap,他的key就是ThreadLocal類型的變量 ThreadLocal的set方法會調用ThreadLocalMap的方法,看下方法的定義spa
private void set(ThreadLocal<?> key, Object value)
複製代碼
這裏能夠看出一個Thread能夠存儲不一樣類型的數據。線程
這裏注意一點引發迷惑的就是明明是每一個線程本地的信息,爲何又要定義成 static field,這不是有點矛盾麼?並且爲何又是 typically,那非typically呢?code
static變量是類靜態變量,即一個線程內,static變量是被各個實例共同引用的, 若是把ThreadLocal聲明爲非static變量,那麼每建立一個該類的實例就會建立一個新的實例對象,那麼同一個線程就會訪問到同一個實例對象的不一樣ThreadLocal對象,而分析ThreadLocal類可知,事實上ThreadLocal自己是不存儲數據的,只是對Thread類對象的局部變量進行操做,因此定義爲static更合適,不然雖然不會致使錯誤,也會致使資源浪費。orm
舉例以下, 若是設置爲static型變量
public class ThreadLocalUtils {
private static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
public static String getThreadLocal() {
return stringThreadLocal.get();
}
public static void setThreadLocal(String value) {
stringThreadLocal.set(value);
}
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
for (int i=0; i<4; i++) {
list.add(i);
}
list.parallelStream().forEach(integer -> {
ThreadLocalUtils.setThreadLocal(String.valueOf(integer));
System.out.println("Start-"+Thread.currentThread().getName()+":"+ThreadLocalUtils.getThreadLocal());
try {
Thread.sleep(900);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End-"+Thread.currentThread().getName()+":" + ThreadLocalUtils.getThreadLocal());
});
}
}
複製代碼
輸出結果爲:
Start-ForkJoinPool.commonPool-worker-2:3
Start-ForkJoinPool.commonPool-worker-1:1
Start-ForkJoinPool.commonPool-worker-3:0
Start-main:2
End-main:2
End-ForkJoinPool.commonPool-worker-3:0
End-ForkJoinPool.commonPool-worker-2:3
End-ForkJoinPool.commonPool-worker-1:1
複製代碼
若是設置爲非static類型變量:
public class ThreadLocalUtils {
private ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();
public String getThreadLocal() {
return stringThreadLocal.get();
}
public void setThreadLocal(String value) {
stringThreadLocal.set(value);
}
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
for (int i=0; i<4; i++) {
list.add(i);
}
list.parallelStream().forEach(integer -> {
ThreadLocalUtils utils = new ThreadLocalUtils();
utils.setThreadLocal(String.valueOf(integer));
System.out.println("Start-"+Thread.currentThread().getName()+":"+utils.getThreadLocal());
try {
Thread.sleep(900);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("End-"+Thread.currentThread().getName()+":" + utils.getThreadLocal());
});
}
}
複製代碼
輸出以下:
Start-main:2
Start-ForkJoinPool.commonPool-worker-2:3
Start-ForkJoinPool.commonPool-worker-3:0
Start-ForkJoinPool.commonPool-worker-1:1
End-main:2
End-ForkJoinPool.commonPool-worker-1:1
End-ForkJoinPool.commonPool-worker-2:3
End-ForkJoinPool.commonPool-worker-3:0
複製代碼
二者的輸出結果徹底相同,可是爲非static型變量就會出現類變量的重複定義。
若是將stringThreadLocal變爲String類型變量就會變成
Start-main:3
Start-ForkJoinPool.commonPool-worker-1:1
Start-ForkJoinPool.commonPool-worker-3:0
Start-ForkJoinPool.commonPool-worker-2:3
End-ForkJoinPool.commonPool-worker-3:1
End-main:1
End-ForkJoinPool.commonPool-worker-1:1
End-ForkJoinPool.commonPool-worker-2:1
複製代碼
能夠看到ThreadLocal變量和非ThreadLocal變量的區別了。
那麼非typically的場景是什麼呢,就是在單例模式下,不須要指定爲static:讓類自己成爲一個單例對象,這樣只要全局中有可用的單例對象,就能夠安全地使用實例級ThreadLocal。