在
http://blog.youkuaiyun.com/zero__007/article/details/78288448中简单介绍了一下ThreadLocal,每个ThreadLocal实例都有一个唯一的threadLocalHashCode初始值,在ThreadLocalMap中设置或获取Entry时,会根据threadLocalHashCode&(len-1)的值去对应的槽中操作。
而ThreadLocal解决Hash 冲突使用线性探测的方法,当一个线程对应多个ThreadLocal实例的场景中,在命中的情况下基本上一次hash就可以找到位置,如果发生没有命中的情况,则会引发性能会急剧下降,本身是O(1),结果变成了O(n)当在读写操作频繁的场景,这点导致性能的后滞。
作为一个高并发框架,Netty对ThreadLocal作了一些优化,并提供一个性能更好的FastThreadLocal。
再提一下FastThreadLocal中variablesToRemoveIndex,该变量是static final修饰的,其值就是0,在InternalThreadLocalMap中会占index=0的槽。实际上该下标的元素是一个包装了IndentityHashMap的Set,每次FastThreadLocal中设置值的时候将自己加到该Set,移除值的时候将自己(FastThreadLocal实例)从该Set移除。也就是说初始值、设置值、删除值这几个功能的耗时比有值时的get更多。
关于ThreadLocal与FastThreadLocal的性能对比数据,可以看下http://www.bubuko.com/infodetail-1932175.html
而ThreadLocal解决Hash 冲突使用线性探测的方法,当一个线程对应多个ThreadLocal实例的场景中,在命中的情况下基本上一次hash就可以找到位置,如果发生没有命中的情况,则会引发性能会急剧下降,本身是O(1),结果变成了O(n)当在读写操作频繁的场景,这点导致性能的后滞。
作为一个高并发框架,Netty对ThreadLocal作了一些优化,并提供一个性能更好的FastThreadLocal。
public class FastThreadLocalThread extends Thread {
private InternalThreadLocalMap threadLocalMap;
public FastThreadLocalThread() { }
public FastThreadLocalThread(Runnable target) {
super(target);
}
public FastThreadLocalThread(ThreadGroup group, Runnable target) {
super(group, target);
}
public FastThreadLocalThread(String name) {
super(name);
}
public FastThreadLocalThread(ThreadGroup group, String name) {
super(group, name);
}
public FastThreadLocalThread(Runnable target, String name) {
super(target, name);
}
public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {
super(group, target, name);
}
public FastThreadLocalThread(ThreadGroup group, Runnable target, String name, long stackSize) {
super(group, target, name, stackSize);
}
/**
* Returns the internal data structure that keeps the thread-local variables bound to this thread.
* Note that this method is for internal use only, and thus is subject to change at any time.
*/
public final InternalThreadLocalMap threadLocalMap() {
return threadLocalMap;
}
/**
* Sets the internal data structure that keeps the thread-local variables bound to this thread.
* Note that this method is for internal use only, and thus is subject to change at any time.
*/
public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {
this.threadLocalMap = threadLocalMap;
}
}
Netty专门提供一个FastThreadLocalThread,继承了JDK的Thread,内部也有个InternalThreadLocalMap实例变量,并暴露了这个变量的getter/setter方法。
JDK中的ThreadLocalMap中的数组存放的是Entrty,key为ThreadLocal,value为真正保存的变量,而InternalThreadLocalMap中的数组里面的数据就是真正保存的变量值。
public class FastThreadLocal<V> {
private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
private final int index;
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
//……
}
JDK的ThreadLocal中将threadLocalHashCode放在了ThreadLocal类中,然后再拿这个值通过计算去定位ThreadLocalMap中的槽,而Netty的FastThreadLocal直接直接保存index,该index是通过InternalThreadLocalMap的静态方法获取的,该值是一个递增的值,也就是说不同的线程,在同一个FastThreadLocal中保存的变量值,在其实例变量InternalThreadLocalMap中的槽都是一样的。
public class Test {
static FastThreadLocal<String> threadLocal = new FastThreadLocal<String>(){
@Override
protected String initialValue() throws Exception {
return "zero";
}
};
public static void main(String[] args) {
FastThreadLocalThread thread0 = new FastThreadLocalThread(new Runnable() {
@Override
public void run() {
System.out.println(threadLocal.get());
}
});
FastThreadLocalThread thread1 = new FastThreadLocalThread(new Runnable() {
@Override
public void run() {
System.out.println(threadLocal.get());
}
});
thread0.start();
thread1.start();
}
}
上面的例子,线程私有的String变量,在线程的实例变量threadLocalMap里的数组,存放的位置下标都是1。由于InternalThreadLocal的index会递增,因此InternalThreadLocal实例再多的话也只会扩容,而不会发生Hash冲突。
再提一下FastThreadLocal中variablesToRemoveIndex,该变量是static final修饰的,其值就是0,在InternalThreadLocalMap中会占index=0的槽。实际上该下标的元素是一个包装了IndentityHashMap的Set,每次FastThreadLocal中设置值的时候将自己加到该Set,移除值的时候将自己(FastThreadLocal实例)从该Set移除。也就是说初始值、设置值、删除值这几个功能的耗时比有值时的get更多。
关于ThreadLocal与FastThreadLocal的性能对比数据,可以看下http://www.bubuko.com/infodetail-1932175.html