在文章ThreadLocal与FastThreadLocal中我们介绍了JDK的ThreadLocal与Netty的FastThreadLocal之间的一点区别.
这篇文章在单独介绍下FastThreadLocal.
代码语言:javascript复制io.netty.util.concurrent.FastThreadLocal
我们先看个对比表格
JDK | Netty |
---|---|
Thread | FastThreadLocalThread |
ThreadLocal | FastThreadLocal |
ThreadLocalMap | InternalThreadLocalMap |
在Netty中,每个被创建的FastThreadLocal对象,都与唯一的一个index值所绑定,比如说在JVM中,第一个被创建的FastThreadLocal,它的index属性值=1,第二个被创建的FastThreadLocal,它的index属性值=2,第120个被创建的FastThreadLocal,它的index属性值=120,以此类推
于是,每个FastThreadLocal作为key(实际是作为数组下标),被存放到FastThreadLocalThread的InternalThreadLocalMap中时,它的位置已经被固定了.
代码语言:javascript复制// FastThreadLocal构造器
private final int index;
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
// 静态属性
static final AtomicInteger nextIndex = new AtomicInteger();
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
return index;
}
在InternalThreadLocalMap内部,底层使用的是对象数组存储数据的.
代码语言:javascript复制// 底层使用数组存储数据
Object[] indexedVariables;
测试代码如下
代码语言:javascript复制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;
}
};
代码语言:javascript复制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();
我们创建了两个静态的FastThreadLocal对象,它们的索引分别是1和2,因此它们被存放到了线程的InternalThreadLocalMap属性的下标1和2的位置.
这时候我们关注的另一个点是,这个InternalThreadLocalMap,它的底层是数组,用来存储数据的.数组默认大小32.
代码语言:javascript复制private static Object[] newIndexedVariableTable() {
Object[] array = new Object[32];
Arrays.fill(array, UNSET);
return array;
}
由于我们的测试代码在整个JVM中只是创建了2个FastThreadLocal对象.而测试的两个线程也只是使用了这2个FastThreadLocal对象.
我们现在测试这么一个场景,在JVM中,在其他的地方创建了200个FastThreadLocal对象,在创建完这200个FastThreadLocal对象之后,又创建了我们的那2个FastThreadLocal对象.也就是说我们的那2个FastThreadLocal对象的索引分别是201和202.
代码语言:javascript复制static {
// 模拟前提前创建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整个数组大小是256
线程为了存放2个FastThreadLocal对象,需要使用256长度的数组,这无疑是一种空间换时间.