我们都知道在jdk中有一个ThreadLocal,让每个线程都有一个独享的变量,Netty也是多线程工作的,所有它也需要这么一个机制,不出乎意料,果然他有一个FastThreadLocal,比jdk的ThreadLocal更快,接下来,我们就来感受一下他的源码。下面时代码的入口:
public class FastThreadLocalTest {
private static FastThreadLocal<Object> threadLocal0 = new FastThreadLocal<Object>() {
@Override
protected Object initialValue() {
return new Object();
}
@Override
protected void onRemoval(Object value) throws Exception {
System.out.println("onRemoval");
}
};
public static void main(String[] args) {
new Thread(() -> {
Object object = threadLocal0.get();
// .... do with object
System.out.println(object);
threadLocal0.set(new Object());
}).start();
new Thread(() -> {
Object object = threadLocal0.get();
System.out.println(object);
while (true) {
System.out.println(threadLocal0.get() == object);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
首先我们来看一下他是如何创建的:
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
它的构造方法就只是获得了一个索引,在jvm中FastThreadLocal是以这个索引来区别的,它的生成方式是从0开始递增。
下面来看一下他的get方法:
public final V get() {
return get(InternalThreadLocalMap.get());
}
他首先是从自己内部维护的一个map中调用get方法:
public static InternalThreadLocalMap get() {
//首先获取当前线程
Thread thread = Thread.currentThread();
//获取InternalThreadLocalMap
if (thread instanceof FastThreadLocalThread) {
return fastGet((FastThreadLocalThread) thread);
} else {
return slowGet();
}
}
根据当前线程执行不同的逻辑:
private static InternalThreadLocalMap slowGet() {
//这是一个jdk底层的ThreadLocal
ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
//从ThreadLocal中获得当前线程所对应的InternalThreadLocalMap
InternalThreadLocalMap ret = slowThreadLocalMap.get();
//如果为空需要进行初始化
if (ret == null) {
ret = new InternalThreadLocalMap();
slowThreadLocalMap.set(ret);
}
return ret;
}
我们发现FastThreadLocal底层是利用了jdk的TheadLocal来保存每个线程的InternalThreadLocalMap对象。
public final V get(InternalThreadLocalMap threadLocalMap) {
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return initialize(threadLocalMap);
}
然后从当前线程的threadLocalMap里面根据当前FastTheadLocal下标来获取对象:
public Object indexedVariable(int index) {
Object[] lookup = indexedVariables;
return index < lookup.length? lookup[index] : UNSET;
}
每个线程的threadLocalMap里面维护了一个数组,而数组的下标对应的就是当前FastTheadLocal唯一标识好,就直接根据这个index从数组中取得对象,时间复杂度是O(1),这也就是Netty中FastThreadLocal快的地方,而线程set对象的时候也是根据FastThreadLocal的index将threadLocalMap数组里面对应的位置set上去。