在文章ThreadLocal與FastThreadLocal中我們介紹了JDK的ThreadLocal與Netty的FastThreadLocal之間的一點區(qū)別.
這篇文章在單獨介紹下FastThreadLocal.
io.netty.util.concurrent.FastThreadLocal
我們先看個對比表格
JDK | Netty |
---|---|
Thread | FastThreadLocalThread |
ThreadLocal | FastThreadLocal |
ThreadLocalMap | InternalThreadLocalMap |
在Netty中,每個被創(chuàng)建的FastThreadLocal對象,都與唯一的一個index值所綁定,比如說在JVM中,第一個被創(chuàng)建的FastThreadLocal,它的index屬性值=1,第二個被創(chuàng)建的FastThreadLocal,它的index屬性值=2,第120個被創(chuàng)建的FastThreadLocal,它的index屬性值=120,以此類推
于是,每個FastThreadLocal作為key(實際是作為數(shù)組下標(biāo)),被存放到FastThreadLocalThread的InternalThreadLocalMap中時,它的位置已經(jīng)被固定了.
// FastThreadLocal構(gòu)造器
private final int index;
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
// 靜態(tài)屬性
static final AtomicInteger nextIndex = new AtomicInteger();
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
return index;
}
在InternalThreadLocalMap內(nèi)部,底層使用的是對象數(shù)組存儲數(shù)據(jù)的.
// 底層使用數(shù)組存儲數(shù)據(jù)
Object[] indexedVariables;
測試代碼如下
public static final FastThreadLocal<String> FAST_COMPANY = new FastThreadLocal<String>() {
@Override
protected String initialValue() {
return "FAST_CHINA";
}
};
public static final FastThreadLocal<Integer> FAST_YEAR = new FastThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 2020;
}
};
FastThreadLocalThread t3 = new FastThreadLocalThread(() -> {
System.out.println(Address.FAST_COMPANY.get());
System.out.println(Address.FAST_YEAR.get());
try {
TimeUnit.MINUTES.sleep(15);
} catch (InterruptedException ignored) {
}
}, "thread-3");
t3.start();
FastThreadLocalThread t4 = new FastThreadLocalThread(() -> {
System.out.println(Address.FAST_COMPANY.get());
System.out.println(Address.FAST_YEAR.get());
try {
TimeUnit.MINUTES.sleep(15);
} catch (InterruptedException ignored) {
}
}, "thread-4");
t4.start();
我們創(chuàng)建了兩個靜態(tài)的FastThreadLocal對象,它們的索引分別是1和2,因此它們被存放到了線程的InternalThreadLocalMap屬性的下標(biāo)1和2的位置.
這時候我們關(guān)注的另一個點是,這個InternalThreadLocalMap,它的底層是數(shù)組,用來存儲數(shù)據(jù)的.數(shù)組默認(rèn)大小32.
private static Object[] newIndexedVariableTable() {
Object[] array = new Object[32];
Arrays.fill(array, UNSET);
return array;
}
由于我們的測試代碼在整個JVM中只是創(chuàng)建了2個FastThreadLocal對象.而測試的兩個線程也只是使用了這2個FastThreadLocal對象.
我們現(xiàn)在測試這么一個場景,在JVM中,在其他的地方創(chuàng)建了200個FastThreadLocal對象,在創(chuàng)建完這200個FastThreadLocal對象之后,又創(chuàng)建了我們的那2個FastThreadLocal對象.也就是說我們的那2個FastThreadLocal對象的索引分別是201和202.
static {
// 模擬前提前創(chuàng)建200個FastThreadLocal對象
for (int i =0;i <200;i++) {
final int j = i;
FastThreadLocal<String> FAST_COMPANY = new FastThreadLocal<String>() {
@Override
protected String initialValue() {
return "FAST_CHINA" + j;
}
};
}
}
public static void main(String[] args) {
FastThreadLocalThread t3 = new FastThreadLocalThread(() -> {
System.out.println(Address.FAST_COMPANY.get());
System.out.println(Address.FAST_YEAR.get());
try {
TimeUnit.MINUTES.sleep(15);
} catch (InterruptedException ignored) {
}
}, "thread-3");
t3.start();
FastThreadLocalThread t4 = new FastThreadLocalThread(() -> {
System.out.println(Address.FAST_COMPANY.get());
System.out.println(Address.FAST_YEAR.get());
try {
TimeUnit.MINUTES.sleep(15);
} catch (InterruptedException ignored) {
}
}, "thread-4");
t4.start();
}
再次測試我們的代碼
2個FastThreadLocal對象被存放在索引201和202位置.
InternalThreadLocalMap整個數(shù)組大小是256
線程為了存放2個FastThreadLocal對象,需要使用256長度的數(shù)組,這無疑是一種空間換時間.
公眾號: Netty歷險記