FastThreadLocal是netty中存放线程本地变量的工具类,是对JDK中的ThreadLocal的改良,在netty中使用FastThreadLocal替代ThreadLocal的使用。
FastThreadLocal使用示例:
public class TestFastThreadLocal {
public static FastThreadLocal<Object> fastThreadLocal = new FastThreadLocal<Object>() {
@Override
protected Object initialValue() throws Exception {
return new Object();
}
};
public static void main(String[] args) {
new FastThreadLocalThread(() -> {
Object object = fastThreadLocal.get();
System.out.println(object);
}).start();
new FastThreadLocalThread(() -> {
Object object = fastThreadLocal.get();
System.out.println(object);
}).start();
}
}
使用关键点如下:
- new FastThreadLocal实例,重写initialValue方法,在调用其get方法获取不到对象时就会调用initialValue方法。
- new FastTHreadLocalThread实例,调用get获取当前线程的本地变量
FastThreadLocal源码分析:
-
首先看下其中的重要属性和方法
FastThreadLocal类图.png
在这里我们主要关注下其中的index属性、get()方法和set()方法 -
index属性是用来标志当前FastThreadLocal为JVM全局唯一,也是保证FastThreadLocal高性能的基础。
private final int index;
//创建FastThreadLocal实例的时候会设置其index属性
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
if (index < 0) {
nextIndex.decrementAndGet();
throw new IllegalStateException("too many thread-local indexed variables");
}
return index;
}
static final AtomicInteger nextIndex = new AtomicInteger();
在创建FastThreadLocal实例的时候会设置iindex属性,其通过一个Integer原子类属性nextIndex的自增来获取,保证FastThreadLocal在一个JVM实例中的唯一。
- FastThreadLocal.get()方法,通过其get方法可以获取当前线程保存的本地变量。
//获取当前线程的本地变量
public final V get() {
//获取当前线程的InternalThreadLocalMap
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
//通过当前线程的ThreadLocalMap的indexedVariable的index属性来获取当前线程本地变量
Object v = threadLocalMap.indexedVariable(index);
//如果对象不是默认的unset的返回
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
//如果返回对象为默认的UNSET的Object对象,调用自定义的initialize方法创建对象
return initialize(threadLocalMap);
}
//获取当前线程的InternalThreadLocalMap
public static InternalThreadLocalMap get() {
//获取当前线程
Thread thread = Thread.currentThread();
//判断当前线程是否为FastThreadLocalThread,是则用采用FastThreadLocal中获取线程本地变量
if (thread instanceof FastThreadLocalThread) {
return fastGet((FastThreadLocalThread) thread);
} else {
//否则从ThreadLocal中获取当前线程本地变量
return slowGet();
}
}
//从FastThreadLocalThread中获取当前线程的InternalThreadLocalMap对象
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
if (threadLocalMap == null) {
thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
}
return threadLocalMap;
}
//从ThreadLocal中获取当前线程的InternalThreadLocalMap对象
private static InternalThreadLocalMap slowGet() {
ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
InternalThreadLocalMap ret = slowThreadLocalMap.get();
if (ret == null) {
ret = new InternalThreadLocalMap();
slowThreadLocalMap.set(ret);
}
return ret;
}
//在UnpaddedInternalThreadLocalMap中有个ThreadLocal包装的静态变量
static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();
//通过FastThreadLocal的index来获取当前线程的本地变量
public Object indexedVariable(int index) {
Object[] lookup = indexedVariables;
return index < lookup.length? lookup[index] : UNSET;
}
//存放Object的数组,通过index属性从数组中获取对应索引下的对象
Object[] indexedVariables;
- 首先获取当前线程的InternalThreadLocalMap对象,FastThreadLocalThread线程中有个threadLocalMap属性,普通线程就从slowThreadLocalMap属性中的ThreadLocal中获取InternalThreadLocalMap对象。
- 根据FastThreadLocal对象的index属性,从InternalThreadLocalMap中获取线程本地变量。在创建FastThreadLocal实例的时候,就生成了该对象实例的index属性,保证了在一个JVM实例中的唯一性。在InternalThreadLocalMap中是有个一个Object[]数组,然后通过index从数组中获取当前线程保存的本地变量。
FastThreadLocal较ThreadLocal高效的原因:在FastThreadLocalThread线程中有个InternalThreadLocalMap类型的属性threadLocalMap,InternalThreadLocalMap里面有个indexedVariables属性是通过Object[]数组来保存线程本地变量信息。FastThreadLocal通过index属性值来定位Object[]数组中的元素。但是ThreadLocal的原理是每次拿到key的hashCode,然后和ThreadLocalMap中的Entry数组长度进行取余,然后再放到ThreadLocalMap中。
- FastThreadLocal.set()方法,set方法看起来简单,其实做的事情也不少
public final void set(V value) {
//当前value对象不是UNSET对象,获取当前线程的InternalThreadLocalMap对象,设置value
if (value != InternalThreadLocalMap.UNSET) {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
setKnownNotUnset(threadLocalMap, value);
} else {
remove();
}
}
//设置当前线程FastThreadLocal变量,并且添加到remove set中
private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
if (threadLocalMap.setIndexedVariable(index, value)) {
addToVariablesToRemove(threadLocalMap, this);
}
}
public boolean setIndexedVariable(int index, Object value) {
Object[] lookup = indexedVariables;
if (index < lookup.length) {
Object oldValue = lookup[index];
lookup[index] = value;
return oldValue == UNSET;
} else {
expandIndexedVariableTableAndSet(index, value);
return true;
}
}
private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
//获取InternalThreadLocalMap中的indexedVariable数组的下标为0的对象(待删除的FastThreadLocal set对象)
Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
Set<FastThreadLocal<?>> variablesToRemove;
if (v == InternalThreadLocalMap.UNSET || v == null) {
variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
} else {
variablesToRemove = (Set<FastThreadLocal<?>>) v;
}
variablesToRemove.add(variable);
}
- 判断value是否为UNSET对象,是则删除当前FastThreadLocal对象。否则获取当前线程的InternalThreadLocalMap对象,设置当前线程本地变量
- 设置当前线程本地变量,初始indexedVariable数组是32位,长度不够就进行扩容,然后把当前线程的其他FastThreadLocal对象转移过去,因为一个线程可能有多个线程本地变量,也即多个FastThreadLocal对象。
- 在进行了indexedVariable数组扩容或者替换了原有UNSET对象时,会返回true,就会将当前的FastThreadLocal对象添加到待删除集合中,这个是放置在indexedVariable数组的索引下标为0的位置。他是个Set<FastThreadLocal<?>> 集合,存放的是当前线程的所有线程本地变量FastThreadLocal对象实例。
为什么这里要使用Set来保存当前线程的所有本地变量?
因为使用set来保存,是为了方便删除线程的本地变量,在removeAll的时候,把set转换成数组,便利数组就可以方便删除当前线程的所有本地变量,不需要再进行遍历indexedVariable数组,在本地线程变量比较多的时候,index索引会很散,处理起来效率不高。
网友评论