在activeJDBC框架內部的實作中看到了 ThreadLocal 這個類,記錄下了每個執行緒獨有的連接
private static final ThreadLocal<HashMap<String, Connection>> connectionsTL = new ThreadLocal<>();
感覺是個知識點,就打開原始碼看看了,先看一下原始碼里的解釋
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).
這個鳥文,瞎翻譯一下,就是:
這個類提供了供執行緒專享的變數,這些變數不同與其它普通的變數,它是每個執行緒都有一個自己的獨立初始化的變數(通過get和set方法實作),這個類的實體常用于類的私有靜態欄位,以實作每個執行緒都有自己的狀態(例如userId,事務ID等),
先跑一下用法吧,
package com.test.threadlocal;
public class TestController {
private static int index = 0;
private static String str = "這個字串是每個執行緒共享的";
// 這個變數,看似是一個類的靜態屬性,實則是每個執行緒有自己獨有的區域
private static ThreadLocal<String> threadStr = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "main執行緒專享";
}
};
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 3; i++) {
Thread t = new MyThread();
t.start();
t.join();
}
System.out.println(str);
System.out.println(threadStr.get());
}
static class MyThread extends Thread{
@Override
public void run() {
index++;
str = "第" + index + "個str";
threadStr.set("第" + index + "個threadStr");
}
}
}
這個例子中,從str和threadStr變數的列印結果可以看出來,str被所有的執行緒讀和寫,threadStr在每個執行緒內部開辟了一塊執行緒專享的區域,接下來,我們看一下具體實作,
先看一下建構式
/**
* 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();
/**
* Creates a thread local variable.
* @see #withInitial(java.util.function.Supplier)
*/
public ThreadLocal() {
}
建構式是空的,但是,該類有一個私有整型常量threadLocalHashCode,nextHashCode()方法我們就不看了,省的一如原始碼深似海,看鳥文的話,大概就是每new一個ThreadLocal變數的時候,就會生成一個散列碼,該碼非極端情況下與某個整數取模后不容易沖突(這句話有點迷吧,其實我也不懂)
然后看一下set方法
/**
* 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);
}
容易看出,這個方法設定每個執行緒自己的value,相當于當前執行緒是key,然后得出一個ThreadLocalMap,顯然,這個map用來保存執行緒內部的值,既然是map當然每個執行緒可以保存多個數值了,該map的value我們猜一下就是我要保存的具體的值,估計是用Object類宣告的,那key是什么呢?我們看下ThreadLocalMap類的構造方法,
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
/**
* 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);
}
我的天!這個類沒有繼承我們想象中的HashMap,或者是ConcurrentMap,但是看過,Map的內部實作的同學應該可以發現,這個Map的實作和HashMap的實作簡直就是小巫見大巫,有沒有,它在建構式中做了如下幾步:
- 初始化一個大小為16的Entry陣列
- 通過上面說過的很迷的HashCode散列值與15取模得到將要存盤在陣列中的索引值
- 構造Entry,然后保存進去
- 長度設定為1
- 設定要擴容的限制大小為16的2/3
我們看到這個不就是用陣列實作的Map嘛,看過HashMap實作的我們,覺得灑灑水啦,
Map的set和get方法就不分析了,ThreadLocal的get方法我們還是要貼出來的,畢竟是我們主要分析的東西
/**
* 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) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
可見,是獲取到當前執行緒,用作key獲取到Map,然后用當前this獲取到Entry物體,最后當然獲取到了存盤的value,
我編碼,我快樂~
本文由博客一文多發平臺 OpenWrite 發布!
我的博客即將同步至騰訊云+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=19l4zaaz7g6ct
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/106003.html
標籤:其他
上一篇:一夜之間火爆GitHub的好文!!阿里資深架構師整理分享(內部)的SpringSecurity實戰技術檔案,看完我立馬跪了!!
下一篇:Docker 網路管理
