• 并发之ThreadLocal


    ThreadLocal
    ThreadLocal 用一种存储变量与线程绑定的方式,在每个线程中用自己的 ThreadLocalMap 安全隔离变量,为解决多线程程序的并发问题提供了一种新的思路。
     
    简单画一个UML:
     再看下ThreadLocal源码get()与set()
    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();
        }
    
    public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    set(T value):每次获取当前线程,从线程中获取其属性map,然后以当前threadLocal对象作为key,存放value;

    get():每次获取当前线程,从线程中获取其属性map,然后以当前threadLocal对象作为key,获取value;

    看下原理图:
     

    从上面的结构图,我们已经窥见ThreadLocal的核心机制:

    • 每个Thread线程内部都有一个Map。
    • Map里面存储线程本地对象(key)和线程的变量副本(value)
    • 但是,Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。
    ThreadLocal 弱引用
    从上图UML可看出,Entry
    static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }

    entry对象中存储了ThreadLocal对象的弱引用和这个ThreadLocal对应的value对象的强引用

     分析:
    假设我们在线程的run方法中调用了一个方法,并在这个方法中创建了ThreadLocal对象,并使用了他,内存结构示意图如下。

    当这个方法结束时,这个方法中创建的ThreadLocal对象本身(图中绿色区域)就被垃圾回收器回收了,但是线程还没有结束,

    所以ThreadLocalMap中还存在这个entry。由于entry中的key(即ThreadLocal对象)是弱引用类型,

    所以此时调用entry.get()方法时就会返回null,内部结构如下图所示。

    从图中我们可以看到value对象(红色区域)始终不能被回收,而我们再也不会使用它了,这就造成了内存泄露。

    那Entry中为什么保存的是key的弱引用呢?其实这是为了最大程度上减少内存泄露,副作用是同时减少哈希表中的冲突。

    当ThreadLocal对象被回收时,对应entry中的key就自动变成null(entry对象本身不为null)。若此后我们调用get,set或remove方法时,

    就会尝试删除key为null的entry,以释放value对象所占用的内存。

    总结

    • 每个ThreadLocal只能保存一个变量副本,如果想要上线一个线程能够保存多个副本以上,就需要创建多个ThreadLocal。
    • ThreadLocal内部的ThreadLocalMap键为弱引用,会有内存泄漏的风险。
    • 适用于无状态,副本变量独立后不影响业务逻辑的高并发场景。如果如果业务逻辑强依赖于副本变量,则不适合用ThreadLocal解决,需要另寻解决方案。

    参考:

    https://www.jianshu.com/p/98b68c97df9b

    http://www.cnblogs.com/nullzx/p/7553538.html

  • 相关阅读:
    Unity Shader 之 渲染流水线
    C# 如何快速取到enum中的枚举数量
    Unity NGUI ScrollView 苹果式滑动
    多元线性回归~ML
    梯度下降~ML
    代价函数~ML
    ML~线性代数~python
    unity 大游戏使用什么框架
    C# Activator.CreateInstance()方法使用
    VSync Count 垂直同步
  • 原文地址:https://www.cnblogs.com/xiaozhuanfeng/p/10501093.html
Copyright © 2020-2023  润新知