FastThreadLocal
继续上篇,类似ThreadLocal
这个里面同样有很多操作,但是基本都是基于InternalThreadLocalMap
的。
首先初始化的时候会获取一个索引,默认是0
,也是将自己放在InternalThreadLocalMap
的数组indexedVariables
里的,是将来用于删除的。所以正常存储数据的索引是从1
开始的。
private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
构造方法
有个index
属性,就是对应的索引值,构造方法的时候就会给索引赋值,而且第一个索引是1
,后面都是根据这个索引来进行存取数据的。
set
如果值不是UNSET
,就获取InternalThreadLocalMap
,然后setKnownNotUnset
设置,否则就remove
删除。
public final void set(V value) {
if (value != InternalThreadLocalMap.UNSET) {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
setKnownNotUnset(threadLocalMap, value);
} else {
remove();
}
}
setKnownNotUnset
将索引和值设置进threadLocalMap
里,返回true表示第一次设置,调用addToVariablesToRemove
将FastThreadLocal
添加到删除集合里。
private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
if (threadLocalMap.setIndexedVariable(index, value)) {//新添加的,而不是更新
addToVariablesToRemove(threadLocalMap, this);//需要添加到删除的set里
}
}
addToVariablesToRemove
获取删除集合,如果不存在就根据IdentityHashMap
创建一个set
集合,IdentityHashMap
只根据引用地址判断时是不是同一个。然后将set
集合放入threadLocalMap
数组的0
索引位置,将FastThreadLocal
放进set
集合。
private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);//获取删除set集合
Set<FastThreadLocal<?>> variablesToRemove;//定义set集合
if (v == InternalThreadLocalMap.UNSET || v == null) {//如果set为空的话,就根据map创建一个
variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);//将set添加到threadLocalMap里
} else {
variablesToRemove = (Set<FastThreadLocal<?>>) v;
}
variablesToRemove.add(variable);//将FastThreadLocal添加到set集合
}
remove
删除尝试获取的InternalThreadLocalMap
。
public final void remove() {
remove(InternalThreadLocalMap.getIfSet());
}
将当前FastThreadLocal
对象从set
集合里删除,并把数组位置上的对象删除,设置回UNSET
。这里的onRemoval
不一定会执行。
public final void remove(InternalThreadLocalMap threadLocalMap) {
if (threadLocalMap == null) {
return;
}
Object v = threadLocalMap.removeIndexedVariable(index);//获取删除的对象,也可能是UNSET
removeFromVariablesToRemove(threadLocalMap, this);//从set集合中删除当前FastThreadLocal
if (v != InternalThreadLocalMap.UNSET) {//不是UNSET才处理
try {
onRemoval((V) v);//不一定能触发的方法,空实现
} catch (Exception e) {
PlatformDependent.throwException(e);
}
}
}
removeFromVariablesToRemove
将当前的FastThreadLocal
从set
里删除。
private static void removeFromVariablesToRemove(
InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);//获取set集合
if (v == InternalThreadLocalMap.UNSET || v == null) {//还没初始化
return;
}
@SuppressWarnings("unchecked")
Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
variablesToRemove.remove(variable);//从set中删除
}
get
根据index
获取InternalThreadLocalMap
,获取值,如果不是UNSET
就返回,否则返回初始化的值,默认null
。
public final V get() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
return initialize(threadLocalMap);
}
initialize
设置初始值然后放进threadLocalMap,添加FastThreadLocal
到set
集合中。
private V initialize(InternalThreadLocalMap threadLocalMap) {
V v = null;
try {
v = initialValue();//默认null
} catch (Exception e) {
PlatformDependent.throwException(e);
}
threadLocalMap.setIndexedVariable(index, v);
addToVariablesToRemove(threadLocalMap, this);
return v;
}
protected V initialValue() throws Exception {
return null;
}
getIfExists
调用InternalThreadLocalMap
的getIfSet
获取threadLocalMap
,如果获取到了并且值不为UNSET
就返回index
对应的值,否则就null
。因为初始化的时候值都是UNSET
,如果没有设置过就获取,得到的就是UNSET
,所以也要返回null
。
public final V getIfExists() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
if (threadLocalMap != null) {
Object v = threadLocalMap.indexedVariable(index);
if (v != InternalThreadLocalMap.UNSET) {
return (V) v;
}
}
return null;
}
destroy
非FastThreadLocalThread
线程的时候要调用,把不用得ThreadLocal
删除,不然可能内存泄露了。
public static void destroy() {
InternalThreadLocalMap.destroy();
}
removeAll
在其它容器环境中,可以将FastThreadLocal
全部删除。
public static void removeAll() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
if (threadLocalMap == null) {
return;
}
try {
Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);//获取set集合
if (v != null && v != InternalThreadLocalMap.UNSET) {
@SuppressWarnings("unchecked")
Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
FastThreadLocal<?>[] variablesToRemoveArray =
variablesToRemove.toArray(new FastThreadLocal[0]);//将set转换成数组
for (FastThreadLocal<?> tlv: variablesToRemoveArray) {
tlv.remove(threadLocalMap);//每个FastThreadLocal都删除
}
}
} finally {
InternalThreadLocalMap.remove();//将InternalThreadLocalMap删除
}
}
这个removeAll
还时候很有用的,可以在线程执行完了的时候调用,把里面的线程本地变量都释放了。比如下面这个,其实netty
的线程工厂里的线程任务都是用FastThreadLocalRunnable
包装起来的:
FastThreadLocalThread
这个其实没什么特别的,就是创建的时候如果有Runnable
任务传进来,就可以被包装成FastThreadLocalRunnable
,在完成任务后可以释放所有FastThreadLocal
:
public class FastThreadLocalThread extends Thread {
// This will be set to true if we have a chance to wrap the Runnable.
private final boolean cleanupFastThreadLocals;//是否对Runnable包装成FastThreadLocalRunnable
private InternalThreadLocalMap threadLocalMap;
public FastThreadLocalThread() {
cleanupFastThreadLocals = false;
}
public FastThreadLocalThread(Runnable target) {
super(FastThreadLocalRunnable.wrap(target));
cleanupFastThreadLocals = true;
}
public FastThreadLocalThread(ThreadGroup group, Runnable target) {
super(group, FastThreadLocalRunnable.wrap(target));
cleanupFastThreadLocals = true;
}
public FastThreadLocalThread(String name) {
super(name);
cleanupFastThreadLocals = false;
}
public FastThreadLocalThread(ThreadGroup group, String name) {
super(group, name);
cleanupFastThreadLocals = false;
}
public FastThreadLocalThread(Runnable target, String name) {
super(FastThreadLocalRunnable.wrap(target), name);
cleanupFastThreadLocals = true;
}
public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {
super(group, FastThreadLocalRunnable.wrap(target), name);
cleanupFastThreadLocals = true;
}
public FastThreadLocalThread(ThreadGroup group, Runnable target, String name, long stackSize) {
super(group, FastThreadLocalRunnable.wrap(target), name, stackSize);
cleanupFastThreadLocals = true;
}
/**
* 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;
}
/**
* Returns {@code true} if {@link FastThreadLocal#removeAll()} will be called once {@link #run()} completes.
*/
@UnstableApi
public boolean willCleanupFastThreadLocals() {
return cleanupFastThreadLocals;
}
/** 是否在任务完成时会进行removeAll
* Returns {@code true} if {@link FastThreadLocal#removeAll()} will be called once {@link Thread#run()} completes.
*/
@UnstableApi
public static boolean willCleanupFastThreadLocals(Thread thread) {
return thread instanceof FastThreadLocalThread &&
((FastThreadLocalThread) thread).willCleanupFastThreadLocals();
}
}
至此有关FastThreadLocal
的都讲完了,其实就是自定义了一个集合来保存对象,用了对象数组,用空间换时间。有了这个的理解,后面我们就可以继续讲内存分配里的PoolThreadCache
啦。
好了,今天就到这里了,希望对学习理解有帮助,大神看见勿喷,仅为自己的学习理解,能力有限,请多包涵。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。