zoukankan      html  css  js  c++  java
  • threadlocal彻底理解,深刻

    本文转自http://blog.csdn.net/huachao1001/article/details/51970237

    ThreadLocal的使用相信大家都比较熟悉,但是ThreadLocal内部是如何做到为不同线程保存不同的副本的呢?能看到这篇文章,说明你也跟我一样好奇。接下来我们一层一层解开ThreadLocal的面纱吧~

    1. 涉及到的几个重要类

    ThreadLocal里面的实现,主要涉及到以下几个重要类:

    • Thread:大家很熟悉的线程类,一个Thread类自然代表一个线程。
    • ThreadLocal:既然本文是要解析ThreadLocal类,自然就离不开这个类啦~。
    • ThreadLocalMap:可以看成一个HashMap,但是它本身具体的实现并没有实现继承HashMap甚至跟java.util.Map都沾不上一点关系。只是内部的实现跟HashMap类似(通过哈希表的方式存储)。
    • ThreadLocalMap.Entry:把它看成是保存键值对的对象,其本质上是一个WeakReference<ThreadLocal>对象。

    主要涉及到的类暂时只有这些,其中ThreadLocalMapThreadLocalMap.Entry的源码解析留到下一篇文章讲,在本文中,我们暂时不去牵扯它们的实现细节,我们只需在心中默默地为ThreadLocalMap打上HashMap的标签,把它暂时先当成HashMap来看待,ThreadLocalMap.Entry看成是保存<键,值>的对象。

    2. ThreadLocal数据存取

    2.1 set函数

    我们知道,在使用ThreadLocal时,首先创建ThreadLocal对象,然后再调用其set(T)T get()方法。我们从这些点切入,首先是构造函数如下:

    public ThreadLocal() {
    }
    • 1
    • 2

    可以看到,构造函数没有任何实现。接下来我们再从set函数切入:

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    代码整体流程很简单:

    先拿到保存键值对的ThreadLocalMap对象实例map,如果map为空(第一次调用的时候map值为null),则去创建一个ThreadLocalMap对象并赋值给map,并把键值对保存到map中。

    当然了,虽然整体流程看起来简单,其内部实现需要我们理清!

    我们看到,首先是拿到当前线程实例t,任何将t作为参数获取ThreadLocalMap对象。为什么需要通过Thread类来获取ThreadLocalMap对象呢?Thread类和ThreadLocalMap有什么联系?这些需要我们去看看getMap(Thread t)函数的实现:

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    • 1
    • 2
    • 3

    我们看到,getMap的实现非常简单!!!仅仅返回Thread实例的threadLocals属性。Thread中的ThreadLocalMap属性声明如下:

    ThreadLocal.ThreadLocalMap threadLocals = null;
    • 1

    我们先理一理,ThreadLocalset(T)函数中,首先是拿到当前线程Thread对象中的ThreadLocalMap对象实例threadLocals ,然后再将需要保存的值保存到threadLocals里面。

    换句话说,每个线程引用的ThreadLocal副本值都是保存在当前线程Thread对象里面的。存储结构为ThreadLocalMap类型,ThreadLocalMap保存的键类型为ThreadLocal,值为副本值

    2.2 get函数

    有了set函数中的解析,我们对get函数就更容易理解了!先看看get函数源码:

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    同样的道理,拿到当前线程Thread对象实例中保存的ThreadLocalMap对象map,然后从map中读取键为this(即ThreadLocal类实例)对应的值。

    如果map不是null,直接从map里面读取就好,如果map==null,那么我们需要对当前线程Thread对象实例中保存的ThreadLocalMap对象new一下。即通过setInitialValue函数来创建,setInitialValue函数具体实现如下:

    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;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码很简单,通过createMap来创建ThreadLocalMap对象,前面set函数里面创建ThreadLocalMap也是通过createMap来的,我们看看createMap具体实现:

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    • 1
    • 2
    • 3

    这下对ThreadLocal的存取机制彻底清楚了吧!接下来一节我们以图形的形式做个总结。

  • 相关阅读:
    Django连接SQL Server配置指引
    Django model update的用法介绍
    Python学习之路并发编程--信号量、事件、队列及生产消费模型
    Python学习之路基础篇--11-12Python基础,函数的装饰器
    Python学习之路基础篇--10Python基础,函数进阶
    Python学习之路基础篇--09Python基础,初识函数
    Python学习之路基础篇--08Python基础+ 文件的基本操作和 注册小作业
    Python学习之路基础篇--07Python基础+编码、集合 和 深浅Copy
    Python学习之路基础篇--06Python基础+dict的学习
    Python学习之路基础篇--05Python基础+列表和元组
  • 原文地址:https://www.cnblogs.com/panxuejun/p/8617041.html
Copyright © 2011-2022 走看看