美文网首页
ThreadLocal

ThreadLocal

作者: seuwt | 来源:发表于2019-07-14 23:57 被阅读0次

    ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

    假如每个线程中都有一个connect变量,各个线程之间对connect变量的访问实际上是没有依赖关系的,即一个线程不需要关心其他线程是否对这个connect进行了修改的。ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。

      但是要注意,虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大。

    第一句是取得当前线程,然后通过getMap(t)方法获取到一个map,map的类型为ThreadLocalMap。然后接着下面获取到<key,value>键值对,注意这里获取键值对传进去的是  this,而不是当前线程t。

    如果获取成功,则返回value值。

    如果map为空,则调用setInitialValue方法返回value。

    ThreadLocalMap getMap(Thread t){

        return t.threadLocals;

    }

    在getMap中,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals。

    成员变量threadLocals是什么:

    ThreadLocal.ThreadLocalMap threadLocals = null;

    实际上就是一个ThreadLocalMap,这个类型是ThreadLocal类的一个内部类

    ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。

    setInitialValue方法的具体实现:

    就是如果map不为空,就设置键值对,为空,再创建Map,看一下createMap的实现:

    ThreadLocal是如何为每个线程创建变量的副本的:

      首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。

      初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。

      然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

      1)实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;

      2)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;

      3)在进行get之前,必须先set,否则会报空指针异常;

    如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。

    如果没有先set的话,即在map中查找不到对应的存储,则会通过调用setInitialValue方法返回i,而在setInitialValue方法中,有一个语句是T value = initialValue(), 而默认情况下,initialValue方法返回的是null。

    ---------------------

    作者:wespten

    来源:CSDN

    原文:https://blog.csdn.net/qq_35029061/article/details/86495625

    版权声明:本文为博主原创文章,转载请附上博文链接!

    ThreadLocal的内部方法

    1.protected T initialValue()

    ①该方法实现只返回 null,并且修饰符为protected,很明显,如果用户想返回初始值不为null,则需要定义线程变量时重写该方法;

    1    /**

    2      * @return the initial value for this thread-local

    3      */

    4    protected T initialValue() {

    5        return null;

    6    }

    复制代码

    1 //定义ThreadLocal时重写initialValue方法,返回用户想要的值

    2 private static ThreadLocal t = new ThreadLocal() {

    3    public Object initialValue() {

    4        A a = new A();

    5        return a;

    6    }

    7 };

    复制代码

    ②返回此线程局部变量的当前线程的初始值。最多在每次访问线程来获得每个线程局部变量时调用此方法一次,即线程第一次使用 get() 方法访问变量的时候。如果线程先于 get 方法调用 set(T) 方法,则不会在线程中再调用 initialValue 方法。

    复制代码

    1    /**

    2      * Returns the value in the current thread's copy of this

    3      * thread-local variable.  If the variable has no value for the

    4      * current thread, it is first initialized to the value returned

    5      * by an invocation of the {@link #initialValue} method.

    6      *

    7      * @return the current thread's value of this thread-local

    8      */

    9    public T get() {

    10        Thread t = Thread.currentThread();

    11        ThreadLocalMap map = getMap(t);

    12        if (map != null) {

    13            ThreadLocalMap.Entry e = map.getEntry(this);

    14            if (e != null)

    15                return (T)e.value;

    16        }

    17        return setInitialValue();

    18    }   

    复制代码

    2.public T get()

    ①在1中已经提到,该方法返回当前线程变量副本。如果这是线程第一次调用该方法,则创建并初始化此副本。

    3.public void set(T value)

    ①ThreadLocal中有个内部静态类ThreadLocalMap,用来存放当前线程变量副本中的值,键为当前线程变量对象,值为用户设的值;

    ②当使用ThreadLocal存值时,首先是获取到当前线程对象,然后获取到当前线程本地变量Map,最后将当前使用的ThreadLocal和传入的值放到Map中,也就是说ThreadLocalMap中存的值是[ThreadLocal对象, 存放的值],这样做的好处是,每个线程都对应一个本地变量的Map,所以一个线程可以存在多个线程本地变量(即不同的ThreadLocal,就如1中所说,可以重写initialValue,返回不同类型的子类)。

    复制代码

    1    /**

    2      * Sets the current thread's copy of this thread-local variable

    3      * to the specified value.  Most subclasses will have no need to

    4      * override this method, relying solely on the {@link #initialValue}

    5      * method to set the values of thread-locals.

    6      *

    7      * @param value the value to be stored in the current thread's copy of

    8      *        this thread-local.

    9      */

    10    public void set(T value) {

    11        Thread t = Thread.currentThread();

    12        ThreadLocalMap map = getMap(t);

    13        if (map != null)

    14            map.set(this, value);

    15        else

    16            createMap(t, value);

    17    }

    复制代码

    4.public void remove()

    ①移除此线程中某个ThreadLocal的值,目的是为了减少内存的占用。如果再次访问此线程局部变量,那么在默认情况下它将拥有其initialValue;

    ②只有从jdk1.5开始才有该方法;

    ③当线程结束后,对应该线程的局部变量将自动被垃圾回收,因此显式调用remove清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

    复制代码

    1      /**

    2      * Removes the current thread's value for this thread-local

    3      * variable.  If this thread-local variable is subsequently

    4      * {@linkplain #get read} by the current thread, its value will be

    5      * reinitialized by invoking its {@link #initialValue} method,

    6      * unless its value is {@linkplain #set set} by the current thread

    7      * in the interim.  This may result in multiple invocations of the

    8      * <tt>initialValue</tt> method in the current thread.

    9      *

    10      * @since 1.5

    11      */

    12      public void remove() {

    13          ThreadLocalMap m = getMap(Thread.currentThread());

    14          if (m != null)

    15              m.remove(this);

    16      }

    复制代码

    三、ThreadLocal和同步机制synchonzied相比

    1.synchonzied同步机制是为了实现同步多线程对相同资源的并发访问控制。同步的主要目的是保证多线程间的数据共享。同步会带来巨大的性能开销,所以同步操作应该是细粒度的(对象中的不同元素使用不同的锁,而不是整个对象一个锁)。如果同步使用得当,带来的性能开销是微不足道的。使用同步真正的风险是复杂性和可能破坏资源安全,而不是性能。

    2.ThreadLocal以空间换取时间,提供了一种非常简便的多线程实现方式。因为多个线程并发访问无需进行等待,所以使用ThreadLocal会获得更大的性能。

    3.ThreadLocal中的对象,通常都是比较小的对象。另外使用ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。

    4.synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

    5.Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。

    相关文章

      网友评论

          本文标题:ThreadLocal

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