一、ThreadLocal
一个类可以通过 ThreadLocal 在当前线程内部创建一个独一无二的副本。通过threadLocal.get()
和threadLocal.set(inst)
可以获取和设置当前线程中这个副本。
换句话说,一个ThreadLocal<T>
对象可以实现不同的线程对应不同的T类型对象。
如下例:
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
threadLocal.set(1);
new Thread(() -> {
threadLocal.set(2);
System.out.println(threadLocal.get());
}).start();
Thread.sleep(10);
System.out.println(threadLocal.get());
以上代码打印结果为:
2
1
ThreadLocal<T>
有两个公开方法,get()
和set(T)
。
-
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) {
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
-
ThreadLocal.set(T)
:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocal.getMap(Thread)
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看到,ThreadLocal的原理其实很简单:在每个线程Thread
中,都有一个ThreadLocal.ThreadLocalMap
类型的变量;这是一个以ThreadLocal<T>
对象为key,以对应的T类型对象为value的map。也就是说,当使用ThreadLocal.set(T)
保存对象时,实际上存储的地方是线程的内部。
虽然是在线程内部,key为ThreadLocal,value为T的map,但是也可以换个角度来看:
每一个ThreadLocal对象,都相当于保存了一个以线程Thread
为key,以对应类T的实例为value的map;而这个map是绝对线程安全的,并且规避了同步/加锁的开销。
要注意这是一个ThreadLocal对象。如果创建了多个ThreadLocal对象,那么每个对象对应的值可以是不同的。
ThreadLocal<Integer> t1 = new ThreadLocal<>();
ThreadLocal<Integer> t2 = new ThreadLocal<>();
t1.set(1);
t2.set(2);
System.out.println(t1.get() + ", " + t2.get());
输出
1, 2
二、Android中的ThreadLocal与Looper
在Android中,之所以能够实现每个线程对应一个Looper对象,其原理的就是使用了ThreadLocal。
在Looper中,有一个静态成员sThreadLocal
:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
看Looper的prepare()
和myLooper()
方法:
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
就是ThreadLocal的赋值与获取过程。
网友评论