zoukankan      html  css  js  c++  java
  • 【原创】源码角度分析Android的消息机制系列(三)——ThreadLocal的工作原理

    ι 版权声明:本文为博主原创文章,未经博主允许不得转载。

    先看Android源码(API24)中对ThreadLocal的定义:

    public class ThreadLocal<T> 

    即ThreadLoca是一个泛型类,再看对该类的注释:

    /**
     * This class provides thread-local variables.  These variables differ from
     * their normal counterparts in that each thread that accesses one (via its
     * <tt>get</tt> or <tt>set</tt> method) has its own, independently initialized
     * copy of the variable.  <tt>ThreadLocal</tt> instances are typically private
     * static fields in classes that wish to associate state with a thread (e.g.,
     * a user ID or Transaction ID).
     *
     * <p>For example, the class below generates unique identifiers local to each
     * thread.
     * A thread's id is assigned the first time it invokes <tt>ThreadId.get()</tt>
     * and remains unchanged on subsequent calls.
     * <pre>
     * import java.util.concurrent.atomic.AtomicInteger;
     *
     * public class ThreadId {
     *     // Atomic integer containing the next thread ID to be assigned
     *     private static final AtomicInteger nextId = new AtomicInteger(0);
     *
     *     // Thread local variable containing each thread's ID
     *     private static final ThreadLocal&lt;Integer> threadId =
     *         new ThreadLocal&lt;Integer>() {
     *             &#64;Override protected Integer initialValue() {
     *                 return nextId.getAndIncrement();
     *         }
     *     };
     *
     *     // Returns the current thread's unique ID, assigning it if necessary
     *     public static int get() {
     *         return threadId.get();
     *     }
     * }
     * </pre>
     * <p>Each thread holds an implicit reference to its copy of a thread-local
     * variable as long as the thread is alive and the <tt>ThreadLocal</tt>
     * instance is accessible; after a thread goes away, all of its copies of
     * thread-local instances are subject to garbage collection (unless other
     * references to these copies exist).
     *
     * @author  Josh Bloch and Doug Lea
     * @since   1.2
     */

    也就是说,ThreadLocal类提供一个thread-local的变量,但是这个变量在每个线程中的副本是不同的,每个线程独立地使用thread-local变量在自己线程中的副本。ThreadLocal的实例是private static的,并且该实例是和一个线程的状态相关的。每个线程持有thread-local变量的弱引用。线程死亡,线程中所有thread-local实例的副本会被GC回收(除非该副本存在一些其他引用。因为GC回收一个对象的判定标准是,该对象不存在任何引用或被引用的关系)。

    只需要弄清楚ThreadLocal的get和set方法,就可以明白其工作原理了。

    先看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);
        }

    value即要存储的数据。ThreadLocalMap 是ThreadLocal中的一个内部类,主要用来存储threadLocal中的数据,下面会详细说明。通过上面这段代码,我们可以知道,set方法首先会获取当前线程的ThreadLocalMap。如果map不为空,则直接更新数据;否则,创建ThreadLocalMap,同时将value值放入该map中。

    若想要给thread-local变量一个初始值的话,不需要重写set方法,直接重写initialValue方法即可。

    protected T initialValue() {
        return null;
    }

    一般情况下,当调用get方法时,该方法才会被第一次调用,除非在调用get方法之前,先调用了set方法。

    下面我们来看下ThreadLocalMap:

        /**
         * ThreadLocalMap is a customized hash map suitable only for
         * maintaining thread local values. No operations are exported
         * outside of the ThreadLocal class. The class is package private to
         * allow declaration of fields in class Thread.  To help deal with
         * very large and long-lived usages, the hash table entries use
         * WeakReferences for keys. However, since reference queues are not
         * used, stale entries are guaranteed to be removed only when
         * the table starts running out of space.
         */
        static class ThreadLocalMap

    ThreadLocalMap是ThreadLocal中的一个静态内部类,为了维护threadLocal中的数据而特意定制的一个hash map。Hash table中的entry使用了弱引用。因为这里没有用引用队列,所以只有当hash table内没有空间了,才会将entry remove出去。

    ThreadLocalMap也有一个静态内部类:

    static class Entry extends WeakReference<ThreadLocal> {
         /** The value associated with this ThreadLocal. */
         Object value;
     
         Entry(ThreadLocal k, Object v) {
             super(k);
             value = v;
         }
     }

    Entry.value即我们存储的数据。

    private Entry[] table;

    我们将存储数据的Entry都存放到该table中了。进而通过对table的管理去管理存储的数据。

    再来看ThreadLocal中的get方法:

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

    通过源码我们可以知道,get方法也是先要获取ThreadLocalMap ,若ThreadLocalMap 不为空,则获取其内部的Entry,由上面我们对set方法的分析可以知道,Entry以弱引用的方式存储了value。若Entry不为空,我们将Entry中的value直接返回,即可获得ThreadLocal中存储的数据;否则,就返回ThreadLocal中的初始化数据。

    由上面对ThreadLocal的set和get方法的分析,我们可以看出,我们操作的始终是当前线程的ThreadLocalMap,存放的数据在Entry中,table中又存放了大量的Entry,对Entry进行管理,而table数组又在当前线程的ThreadLocalMap,所以我们在不同线程中访问同一个ThreadLocal的set和get方法时,它们对ThreadLocal的读/写操作都仅仅是在各自线程的内部而已。这就解释了为什么ThreadLocal可以在多个线程中互不干扰地存储和修改数据了。

  • 相关阅读:
    Python基础教程之第2章 列表和元组
    java最简单的方式实现httpget和httppost请求
    90后女生微信销售案例:预热和成熟
    window.onload与$.ready的差别
    在delphi下TClientSocket的使用技巧 转
    delphi安装 Tclientsocket, Tserversocket控件
    DELPHI SOKET 编程(使用TServerSocket和TClientSocket) 转
    Delphi ServerSocket,ClientSocket示例
    Delphi Socket 阻塞线程下为什么不触发OnRead和OnWrite事件
    delphi TServerSocket阻塞线程单元 实例
  • 原文地址:https://www.cnblogs.com/mengyi/p/7044662.html
Copyright © 2011-2022 走看看