關於java併發程式設計的相關文章都是閱讀了《java併發程式設計實戰》之後的讀書筆記總結
概述
ThreadLocal其實是執行緒封閉的一種規範化的實現,它通過提供一組get和set的介面為每個使用該變數的執行緒儲存一份獨立的副本。對於那種按執行緒多例項(每個執行緒對應一個例項)的物件的訪問,並且這個物件很多地方都要用到的情況(例如資料庫連線管理、會話session管理以及執行緒私有的訊息佇列等),ThreadLocal就會展現出它的魅力。
下面的這個小例子展示了ThreadLocal的常規使用:
public class ThreadResource {
private String threadName;
private int threadId;
public ThreadResource(int threadId, String threadName) {
this.threadId = threadId;
this.threadName = threadName;
}
public String getThreadName() {
return threadName;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
public int getThreadId() {
return threadId;
}
public void setThreadId(int threadId) {
this.threadId = threadId;
}
}
public class Test {
//以一個靜態例項的方式持有一個ThreadLocal物件,它裡面以map的形式儲存了執行緒的區域性變數
static ThreadLocal<ThreadResource> resoursePackage = new ThreadLocal<ThreadResource>() {
@Override
protected ThreadResource initialValue() {
return new ThreadResource(0, "initialThread");
}
};
private static class TestThread extends Thread {
@Override
public void run() {
resoursePackage.set(new ThreadResource(1, "testThread"));
System.out.println(resoursePackage.get().getThreadName() + resoursePackage.get().getThreadId());
}
}
public static void main(String[] args) {
System.out.println(resoursePackage.get().getThreadName() + resoursePackage.get().getThreadId());
new TestThread().start();
}
}複製程式碼
原始碼解析ThreadLocal的實現
- get()方法的實現
public T get() {
//獲取當前threadlocal變數所屬的執行緒
Thread t = Thread.currentThread();
//根據執行緒獲取到一個ThreadLocalMap的物件
ThreadLocalMap map = getMap(t);
//如果執行緒已經繫結了一個ThreadLocalMap物件的話,則從中獲取到裡面所儲存的值,否則使用初始化的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T) e.value;
return result;
}
}
return setInitialValue();
}複製程式碼
我們看一下上面getMap()的方法的實現
ThreadLocalMap getMap(Thread t) {
//返回執行緒的一個ThreadLocalMap的成員變數,下面是該成員變數在threa類中的宣告
//ThreadLocal values pertaining to this thread. This map is maintained by the ThreadLocal class.
//ThreadLocal.ThreadLocalMap threadLocals = ull;
return t.threadLocals;
}複製程式碼
再看一下執行緒尚未繫結ThreadLocalMap物件的時候,呼叫的 setInitialValue() 的方法的實現
private T setInitialValue() {
//initialValue()就是我們在新建一個ThreadLocal變數的時候,實現的protected的那個方法。
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//由此我們可知,執行緒尚未繫結到ThreadLocalMap物件的時候
//ThreadLocal為我們使用在initialValue設定的值初始化了一個物件值,並繫結到該執行緒上。
createMap(t, value);
return value;
}複製程式碼
在上面的程式碼中,我們一直提及到了一個ThreadLocalMap的類,它其實是在ThreadLocal的一個靜態內部類,它是一個ThreadLocal自定義的hash map物件,用於儲存執行緒的區域性變數。在它裡面,又包含了一個Entry的靜態內部類,它裡面就是對應的所要儲存的值。我們看一下原始碼的實現
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
//以ThreadLocal物件作為鍵值,儲存threadlocal變數所包含的值
//我們在ThreadLocal的get方法當中也是根據threadlocal變數取出所儲存的值
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}複製程式碼
- set()方法的實現
public void set(T value) {
//獲取threadlocal物件所屬的執行緒
Thread t = Thread.currentThread();
//獲取執行緒所繫結的ThreadLocalMap物件
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//線上程尚未初始化並繫結ThreadLocalMap物件的時候,使用給定的value值新建一個,並將執行緒與該對物件關聯起來
createMap(t, value);
}複製程式碼
在閱讀了上面的原始碼之後,我們大概已經明白了ThreadLocal是怎麼做到為每個執行緒儲存一份引用物件的拷貝的值的。每個thread物件都會持有一個ThreadLocal.ThreadLocalMap的物件的引用,而我們通過ThreadLocal物件找到了執行緒所持有的這個ThreadLocalMap物件,並往其中新增、移除或獲得我們所要儲存的引用物件的值。
關於ThreadLocalMap裡面實現的自定義的hash map我們可以在ThreadLocal的原始碼中深入瞭解,這裡不做進一步的深入。