ThreadLocal如何保证一个线程只能有一个Looper?

我们都知道在调用Looper.prepare的时候会创建一个Looper,那么是如何保证一个线程只有一个Looper的?

首先要知道Looper中有一个sThreadLocal变量,ThreadLocal用于存储上下文信息

    static final ThreadLocal sThreadLocal = new ThreadLocal();

并且用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的值即;如果map为null,则创建一个HashMap并把节点加入

这样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是线程的上下文,并且唯一,而线程中存有key-value键值对,所以一个sThreadLocal对应一个Looper,并且再次修改Looper是,会抛异常,因为Looper已经存在。

所以一个线程只有一个Looper。

如果对HashMap还不了解的同学,这篇文章可能对你有一定帮助 HashMap原理

你可能感兴趣的:(ThreadLocal如何保证一个线程只能有一个Looper?)