zoukankan      html  css  js  c++  java
  • 并发之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

  • 相关阅读:
    document.ready和window.onload的区别
    那些年,我们坚持着。
    JavaScript去除前后空格
    男孩的梦
    shapefile格式说明及读写代码示例 http://www.gispower.org/article/arcgis/arcother/2008/48/0848115049GB922C13K2I22H7G06A4.html
    如何在C#中加载自己编写的动态链接库(DLL)http://www.kehui.net/index.php/article/read/30/26323
    对象复制
    影像复制程序集在不关闭应用程序的前提下更新程序集
    ibatisnet系列(一) 总览 http://hjf1223.cnblogs.com/archive/2006/04/24/383118.html
    创建项模板模板参数
  • 原文地址:https://www.cnblogs.com/xiaozhuanfeng/p/10501093.html
Copyright © 2011-2022 走看看