FastThreadLocal
基本使用
private static final FastThreadLocal<String> FAST_THREAD_LOCAL = new FastThreadLocal<String>() {
@Override
protected String initialValue() throws Exception {
return "Hello World!";
}
};
public static void main(String[] args) {
String value = FAST_THREAD_LOCAL.get();
System.out.println(value);
}
构造方法
在通过构造方法创建FastThreadLocal时,会在其内部初始化一个索引值index:
// 初始化index 在后面会用到
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
// 初始化时通过 InternalThreadLocalMap 获取索引
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
if (index < 0) {
nextIndex.decrementAndGet();
throw new IllegalStateException("too many thread-local indexed variables");
}
return index;
}
通过这个构造方法我们可以获取一个信息:当前线程每实例化一个FastThreadLocal,都将提供一个自增的index。
FastThreadLocal.get()
public final V get() {
// 获取当前线程变量
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return initialize(threadLocalMap);
}
和ThreadLocal相同,FastThreadLocal的作用也是获取当前线程在该ThreadLocal中存储的变量,不过方式有所不同,继续往下看。
InternalThreadLocalMap.get()
这个方法根据当前线程是否是FastThreadLocalThread来判断是否使用FastThreadLocal:
public static InternalThreadLocalMap get() {
Thread thread = Thread.currentThread();// 当前线程
if (thread instanceof FastThreadLocalThread) {// 判断当前线程是否 FastThreadLocalThread
return fastGet((FastThreadLocalThread) thread);
} else {
return slowGet();// 如果不是FastThreadLocalThread就使用普通方式
}
}
fastGet和slowGet的区别
当前线程是FastThreadLocalThread时,将会为当前线程变量初始化InternalThreadLocalMap(还没初始化时),这里会创建一个InternalThreadLocalMap:
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
if (threadLocalMap == null) {
thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
}
return threadLocalMap;
}
如果当前线程不是FastThreadLocalThread时,将会利用InternalThreadLocalMap中的常量 slowThreadLocalMap 来保存初始化的 InternalThreadLocalMap,等于这里没有持有直接引用,而是把InternalThreadLocalMap保存到了当前线程的线程变量中:
private static InternalThreadLocalMap slowGet() {
InternalThreadLocalMap ret = slowThreadLocalMap.get();
if (ret == null) {
ret = new InternalThreadLocalMap();
slowThreadLocalMap.set(ret);
}
return ret;
}
InternalThreadLocalMap
其内部使用数组的结构,初始化时将会创建实例化一个长度为32的数组(当然长度不足时也会涉及到扩容机制):
private InternalThreadLocalMap() {
indexedVariables = newIndexedVariableTable();
}
private static Object[] newIndexedVariableTable() {
Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE];// 长度32
Arrays.fill(array, UNSET);
return array;
}
前面我们已经知道在FastThreadLocal初始化时已经为其获取了索引index,接下来回到FastThreadLocal的get方法:
public final V get() {
// 获取当前线程变量
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return initialize(threadLocalMap);
}
通过indexedVariable(index)来获取当前FastThreadLocal的线程变量。
其实到这里我们可以发现,Netty在这里创建的Object数组是一种空间换时间的方式,使得对线程变量的使用得到了优化。