我们都知道在调用Looper.prepare的时候会创建一个Looper,那么是如何保证一个线程只有一个Looper的?
首先要知道Looper中有一个sThreadLocal变量,ThreadLocal用于存储上下文信息
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
并且用final static 修饰,所以它是唯一的内容不可变的
了解sThreadLocal是干啥用的后,再来看看prepare
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));
}
先调用sThreadLocal.get()方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); // 1、获取线程的threadLocals变量
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); // 2、取到key为sThreadLocal的节点
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); // 3、map为null或者不存在key为sThreadLocal的节点
}
- 1、首先获取当前线程,再getMap取到当前线程的threadLocals变量,它是一个ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
而ThreadLocalMap 是一个HashMap,那么取到一个HashMap后判断是否为null
- 2、如果不为null,则判断key为sThreadLocal的节点是否存在,存在则返回一个Looper对象或者null
- 3、
private T setInitialValue() {
T value = initialValue(); // 1、得到一个null
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
如果不存在key为sThreadLocal的节点,得到value = null,并把这个value作为sThreadLocal的值即<sThreadLocal,null>;如果map为null,则创建一个HashMap并把<sThreadLocal,null>节点加入
这样get方法就要么取到一个Looper,要么就是null,如果为Looper则抛异常,如果为null,则调用sThreadLocal.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);
}
其实都是把Looper作为sThreadLocal的value值
回到开头说的,怎么保证一个线程只有一个Looper?
因为sThreadLocal是线程的上下文,并且唯一,而线程中存有<sThreadLocal,Looper>key-value键值对,所以一个sThreadLocal对应一个Looper,并且再次修改Looper是,会抛异常,因为Looper已经存在。
所以一个线程只有一个Looper。
如果对HashMap还不了解的同学,这篇文章可能对你有一定帮助 HashMap原理
网友评论