關於java併發編程的相關文章都是閱讀了《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();
}
}複製代碼
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()的方法的實現session
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的靜態內部類,它裏面就是對應的所要存儲的值。咱們看一下源碼的實現ide
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;
}
}
}複製代碼
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對象,並往其中添加、移除或得到咱們所要保存的引用對象的值。this
關於ThreadLocalMap裏面實現的自定義的hash map咱們能夠在ThreadLocal的源碼中深刻了解,這裏不作進一步的深刻。spa