2023-09-13  阅读(2)
原文作者:https://blog.csdn.net/wangwei19871103/category_9681495_2.html 原文地址: https://blog.csdn.net/wangwei19871103/article/details/104378597

FastThreadLocal

继续上篇,类似ThreadLocal这个里面同样有很多操作,但是基本都是基于InternalThreadLocalMap的。
首先初始化的时候会获取一个索引,默认是0,也是将自己放在InternalThreadLocalMap的数组indexedVariables里的,是将来用于删除的。所以正常存储数据的索引是从1开始的。

    private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();

202309132203576931.png

构造方法

有个index属性,就是对应的索引值,构造方法的时候就会给索引赋值,而且第一个索引是1,后面都是根据这个索引来进行存取数据的。

202309132203588022.png

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表示第一次设置,调用addToVariablesToRemoveFastThreadLocal添加到删除集合里。

        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

将当前的FastThreadLocalset里删除。

     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,添加FastThreadLocalset集合中。

      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

调用InternalThreadLocalMapgetIfSet获取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包装起来的:

202309132203598033.png

202309132204004054.png

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] ,回复【面试题】 即可免费领取。

阅读全文