ThreadLocal

Posted ttdevs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal相关的知识,希望对你有一定的参考价值。

0x00 ThreadLocal

查看android Message源码的时候,看到一个类: ThreadLocal。之前没太注意这个类,看的有点迷糊,这里对其做一下总结。

0x01 什么是ThreadLocal

This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. 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).

For example, the class below generates unique identifiers local to each thread. A thread’s id is assigned the first time it invokes ThreadId.get() and remains unchanged on subsequent calls.

 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<Integer> threadId =
         new ThreadLocal<Integer>() 
             @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();
     
 

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the 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).

简单来说,我们在线程中创建的变量,任何一个线程都可以访问并对其修改。但是我们通过LocalThread创建的变量,就只有当前线程可以访问了。

0x02 分析

先看一个下在Thread类中有一个成员变量, ThreadLocal.ThreadLocalMap threadLocals, 这个变量由ThreadLocal维护。

public class Thread implements Runnable 
    ...
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ...

在来看看 ThreadLocal 的几个方法:

public class ThreadLocal<T> 

    // 默认返回null, protected方法,创建的时候可以复写这个方法
    protected T initialValue() 
        return null;
    

    public ThreadLocal() 
    

    public T get() 
        Thread t = Thread.currentThread(); // 当前线程
        ThreadLocalMap map = getMap(t); 
        if (map != null) 
            ThreadLocalMap.Entry e = map.getEntry(this); // 传入的是this
            if (e != null)
                return (T)e.value;
        
        return setInitialValue();
    

    private T setInitialValue() 
        T value = initialValue(); // 如果不复写,就会返回null
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    

    // 设置要保存的值
    public void set(T value) 
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value); // 传入的是this(LocalThread)
        else
            createMap(t, value);
    

    // 从队列中rm掉
    public void remove() 
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
    

    // 返回threadLocals,这个变量在Thread中
    ThreadLocalMap getMap(Thread t) 
        return t.threadLocals;
    

    // Thread中提到,threadLocals在LocalThread中维护
    void createMap(Thread t, T firstValue) 
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    

通过上面两段代码可知:Thread 中有个成员变量 threadLocals,这是一个Map类型,这个变量默认为null,当Thread中使用LocalThread时,会由LocalThread给这个变量初始化,并在其中维护自己的要操作的数据。父类有个Map类型的成员变量,由子类初始化,然后自己可能对其进行操作,是不是很简单的逻辑。

0x03 使用

但是涉及线程时,难免就会有同步的问题。这里以 android.os.Looper 为例:

/**
  * <pre>
  *  class LooperThread extends Thread 
  *      public Handler mHandler;
  *
  *      public void run() 
  *          Looper.prepare();
  *
  *          mHandler = new Handler() 
  *              public void handleMessage(Message msg) 
  *                  // process incoming messages here
  *              
  *          ;
  *
  *          Looper.loop();
  *      
  *  </pre>
  */
public final class Looper 
    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    private static void prepare(boolean quitAllowed) 
        if (sThreadLocal.get() != null) 
            throw new RuntimeException("Only one Looper may be created per thread");
        
        sThreadLocal.set(new Looper(quitAllowed));
    

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: @link #prepare()
     */
    public static void prepareMainLooper() 
        prepare(false);
        synchronized (Looper.class) 
            if (sMainLooper != null) 
                throw new IllegalStateException("The main Looper has already been prepared.");
            
            sMainLooper = myLooper();
        
    
...
    /**
     * Run the message queue in this thread. Be sure to call
     * @link #quit() to end the loop.
     */
    public static void loop() 
        final Looper me = myLooper();
        if (me == null) 
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        
        final MessageQueue queue = me.mQueue;
...
        
    
...
    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() 
        return sThreadLocal.get();
    

注释中演示了如何使用,先是 Looper.prepare(); ,在这其中会创建一个Looper对象,通过LocalThread保存,在需要使用的地方取出。

以上是关于ThreadLocal的主要内容,如果未能解决你的问题,请参考以下文章

ThreadLocal(一)设计ThreadLocal的目的

ThreadLocal 的原理讲述 + 基于ThreadLocal实现MVC中的M层的事务控制

对ThreadLocal的一些理解

ThreadLocal源码解析-Java8

ThreadLocal

手撕面试题ThreadLocal!!!