学习笔记——ThreadLocal要点总结

266 阅读2分钟

基本使用

//创建
ThreadLocal<Object> mThreadLocal = new ThreadLocal<Object>()
//设置值
mThreadLocal.set("");
//获取值
mThreadLocal.get()
//清除
mThreadLocal.remove();

原理分析

ThreadLocal的构造方法如下:

  public 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);
        }
    }
    
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
   }

每一个Thread都会持有一个ThreadLocalMap,它使用和HashMap相同的数据结构存储数据,使用ThreadLocal作为Key值。

static class ThreadLocalMap {
    
    static class Entry extends WeakReference<ThreadLocal<?>> {
        // ThreadLocal 关联的 Value
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
}

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

首先根据当前线程获取ThreadLocalMap,然后取出key值为当前ThreadLocal的数据。 remove方法如下:

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

该方法直接将map里所持有key值为当前Thread的local的数据给清除掉了。

总结

  • 通过ThreadLocal,可以对当前运行的线程设置一些线程私有的变量实现线程间的数据隔离;
  • 每个线程(Thread 实例)都维持着一个 threadLocalsThreadLocal.ThreadLocalMap,它和HashMap采用相同的数据结构;
  • ThreadLocal并不存储数据,它只是作为threadLocals的key值,通过get和set方法管理数据;
  • 一个线程中可以有多个ThreadLocal实例作为key值保存不同的value;
  • 每次取值和赋值操作时,都会获取当前线程,再获取threadLocals,实现线程数据的正确访问。

延伸

为什么在Handler中的ThreadLocal可以确保每个线程有且只有一个Loop——静态常量加私有构造方法实现:

public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
}

public static void prepare() {
        prepare(true);
    }

    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));
    }
  1. 通过静态常量的方法,确保唯一切不可变的key值。
  2. 私有化构造方法,确保在创建Looper时首先校验是否已经存在。 最后需要注意:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就可能会导致内存泄漏,而ThreadLocal是通过弱引用的方式作为key值的的,因此当ThreadLocal的对象被回收屎,即使没有手动删除ThreadLocal也会被回收。