美文网首页
Java并发编程:ThreadLocal的理解

Java并发编程:ThreadLocal的理解

作者: 对面的你 | 来源:发表于2018-09-21 17:59 被阅读10次

    前言

    java内存模型

    java虚拟机,模仿真实的计算机结构,为java中各个变量、实例分配内存空间。主要有:

    1. 方法区:类信息、常量、静态变量、即时编译器编译后的代码数据。方法区包括运行时常量池:字面量、符号引用
    2. 堆:线程共享的内存区域,存放对象实例
    3. 程序计数器:
    4. 虚拟机栈:线程私有数据区域,每个方法执行时都会创建一个栈帧,存储方法的变量表、操作数栈、动态链接、返回值、返回地址等信息。
    5. 本地方法栈:线程私有数据区域,与native代码相关。

    ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。
    虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大。

    ThreadLocal类

    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();
    }
    

    获取当前线程的ThreadLocalMap,获取成功则返回value值,失败则调用setInitialValue()。

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    

    threadLoacls是一个ThreadLoaclMap类型,他是ThreadLoacl类的内部类:

    static class ThreadLocalMap {
    
        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
    
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
        ....
    }
    

    Entry类是ThreadLocalMap的内部类,且继承自WeakReference,并且使用ThreadLoacl类型作为键值。

    private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    1. 在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)
    2. 在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
    3. 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

    通俗说明就是,每个线程在创建的时候都会创建自己ThreadLocal类型的私有对象,通过这个私有对象作为Thread里的map的key值,可以找出对应的value,从而达到线程间value复制且不相互影响的作用。

    相关文章

      网友评论

          本文标题:Java并发编程:ThreadLocal的理解

          本文链接:https://www.haomeiwen.com/subject/qatonftx.html