ThreadLocal

2019-01-11  本文已影响31人  土肥圆的诺诺
每天学习一点点

ThreadLocal在android中属于一个常用的类,比如的Android最重要的Handler消息机制里面的Looper存储也是采用ThreadLocal,开源框架EventBus存储当前线程下的发送事件队列状态也是采用ThreadLocal。

ThreadLocal到底能做什么呢?原理又是什么呢?

点进ThreadLocal的类文件看一下,类上写的注释。

This class provides thread-local variables.  These variables differ from
 * their normal counterparts in that each thread that accesses one (via its
 * {@code get} or {@code set} method) has its own, independently initialized
 * copy of the variable.  {@code ThreadLocal} 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 {@code ThreadId.get()}
 * 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&gt; threadId =
 *         new ThreadLocal&lt;Integer&gt;() {
 *             &#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 {@code ThreadLocal}
 * 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).

大体意思就是ThreadLocal 实现一个线程本地的存储。每个线程都有自己的局部变量。所有线程都共享一个ThreadLocal对象,但是每个线程在访问这些变量的时候能得到不同的值,每个线程可以更改这些变量并且不会影响其他的线程,并且支持null值。

我们看下ThreadLocal的方法有哪些?
我们使用一下ThreadLocal

创建一个ThreadLocal,存入一段文本,看在两个线程取出值是多少。

 local = new ThreadLocal<>();
        local.set("存入测试");
        Thread t1 = new Thread(new Runnable() {


            @Override
            public void run() {

                Log.e(TAG, "t1 get(): " + local.get());
                local.set("t1");
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "t2 get() " + local.get());
            }
        });

        t1.start();
        t2.start();
     Log.e(TAG, "main get(): "+local.get() );
结果

我们发现在t1 t2中取出值都是null,那么我们在t1 t2中存储再取出验证下

 local = new ThreadLocal<>();
        local.set("存入测试");
        Thread t1 = new Thread(new Runnable() {


            @Override
            public void run() {
                local.set("t1");
                Log.e(TAG, "t1 get(): " + local.get());

            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                local.set("t2");
                Log.e(TAG, "t2 get() " + local.get());
            }
        });
        t1.start();
        t2.start();
        Log.e(TAG, "main get(): "+local.get() );
image.png

获取大家好奇,怎么就在拿不到local的值呢,我们上面说到ThreadLocal是每个线程可以访问自己内部的副本变量。

我们看下ThreadLocal是怎么实现这种神奇机制的的?

先看下set方法

public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

我们不去关注细节,大家有时间可以仔细看下源码,我们获取了一个map,进行了判null处理,将值存进去。如果是null就去创建map并将值赋进去。可以理解为key就是当前的线程。
再看一下get()方法。

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

根据线程,取出一个map,并根据类型取出一个Entry,根据泛型强转,并返回,如果没有的话,我们看到一个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;
    }

protected T initialValue() {
        return null;
    }

可以看到如果没查到值initialValue,会默认返回一个null。
其实ThreadLocalMap的源码大家可以看一下,因为代码量较多,我就不粘出来了。我们刚看到的Entry其实是一个WeakReference。

 static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

最后看下remove()方法

         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

其实也就是根据当前线程判断,删除this,这里的this指向的就是ThreadLocal自己。
其实还有很多细节,大家可以研究下源码的时候看一下,想一下
这篇文章写的不错,大家可以看看。
https://www.cnblogs.com/xzwblog/p/7227509.html

上一篇下一篇

猜你喜欢

热点阅读