系列FastThreadLocal目录
前言
Netty作为一个高性能的网络框架,针对自己的业务实现了一些API,比如FastThreadLocalThread和FastThreadLocal等。用来实现Pooled池化内存。
# 一、FastThreadLocalThread Netty自定义了Thread类,用于存储自定义的ThreadLocalMap。
public class FastThreadLocalThread extends Thread {
// This will be set to true if we have a chance to wrap the Runnable.
private final boolean cleanupFastThreadLocals;
// 内部的ThreadLocalMap,用于存储ThreadLocal
private InternalThreadLocalMap threadLocalMap;
二、InternalThreadLocalMap
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(InternalThreadLocalMap.class);
// 内嵌了jdk原生的ThreadLocal
private static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap =
new ThreadLocal<InternalThreadLocalMap>();
// 原子类,实现对indexedVariables数组桶位置的记录
private static final AtomicInteger nextIndex = new AtomicInteger();
// Map的HashTable数组的初始长度
private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;
// 单例,空对象,用来填充HashTable
public static final Object UNSET = new Object();
// ThreadLocal的存储数组
private Object[] indexedVariables;
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 {
// 当插入位置的索引大于HashTable的长度时,就开始扩容
expandIndexedVariableTableAndSet(index, value);
return true;
}
}
// 扩容
private void expandIndexedVariableTableAndSet(int index, Object value) {
Object[] oldArray = indexedVariables;
final int oldCapacity = oldArray.length;
int newCapacity = index;
// 这个算法是按照当前的索引扩容到 2*newCapacity - 1
newCapacity |= newCapacity >>> 1;
newCapacity |= newCapacity >>> 2;
newCapacity |= newCapacity >>> 4;
newCapacity |= newCapacity >>> 8;
newCapacity |= newCapacity >>> 16;
// 2*newCapacity - 1 + 1 其实是扩容2倍。
newCapacity ++;
// 将原数组拷贝到新的数组。
Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
// 将拷贝不满的地方进行初始化
Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
// 然后将值进行添加
newArray[index] = value;
indexedVariables = newArray;
}
// 根据索引拿到指定的ThreadLocal,因为这个Map是存储在线程中的,所以不存在线程安全问题
public Object indexedVariable(int index) {
Object[] lookup = indexedVariables;
return index < lookup.length? lookup[index] : UNSET;
}
三、FastThreadLocal
Netty的作者嫌jdk原生的ThreadLoca在每次调用get方法的时候都得计算一次hash,所以将put进Map的时候,将hash桶的索引记录在了成员变量中。
1、ThreadLocal
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private Entry getEntry(ThreadLocal<?> key) {
/// 这里的一个hash
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
2、FastThreadLocal源码
// 重点索引
private final int index;
public FastThreadLocal() {
// 调用InternalThreadLocalMap的静态方法,拿到原子类所记录的当前插入的索引;因为只要是创建过的FastThreadLocal,肯定已经存到map了
index = InternalThreadLocalMap.nextVariableIndex();
}
/**
* Returns the current value for the current thread
*/
@SuppressWarnings("unchecked")
public final V get() {
//
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
// 根据InternalThreadLocalMap和当前FastThreadLocal记录的索引拿到存的ThreadLocal的值
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return initialize(threadLocalMap);
}
三、PoolThreadLocalCache
PoolThreadLocalCache类是用在池化内存分配的时候的ThreadLocal(PoolThreadLocalCache 继承FastThreadLocal)类,其与PoolThreadCache组成键值对存放在FastThread的InternalThreadLocalMap中。重点是重写了initialValue
方法,第一次想从ThreadLocal中获取缓存的时候,发现为空,就会调用重写的initialValue
方法进行初始化PoolThreadCache。
final class PoolThreadLocalCache extends FastThreadLocal<PoolThreadCache> {
private final boolean useCacheForAllThreads;
PoolThreadLocalCache(boolean useCacheForAllThreads) {
this.useCacheForAllThreads = useCacheForAllThreads;
}
// 重写了父类的initialValue方法,会回调这个方法,进行当前Thread操作池化分配时的缓存ThreadLocal
@Override
protected synchronized PoolThreadCache initialValue() {
final PoolArena<byte[]> heapArena = leastUsedArena(heapArenas);
final PoolArena<ByteBuffer> directArena = leastUsedArena(directArenas);
final Thread current = Thread.currentThread();
if (useCacheForAllThreads || current instanceof FastThreadLocalThread) {
final PoolThreadCache cache = new PoolThreadCache(
heapArena, directArena, smallCacheSize, normalCacheSize,
DEFAULT_MAX_CACHED_BUFFER_CAPACITY, DEFAULT_CACHE_TRIM_INTERVAL);
if (DEFAULT_CACHE_TRIM_INTERVAL_MILLIS > 0) {
final EventExecutor executor = ThreadExecutorMap.currentExecutor();
if (executor != null) {
executor.scheduleAtFixedRate(trimTask, DEFAULT_CACHE_TRIM_INTERVAL_MILLIS,
DEFAULT_CACHE_TRIM_INTERVAL_MILLIS, TimeUnit.MILLISECONDS);
}
}
return cache;
}
// No caching so just use 0 as sizes.
return new PoolThreadCache(heapArena, directArena, 0, 0, 0, 0);
}