源码分析ThreadLocal在Handler中的作用

源码分析ThreadLocal在Handler中怎样保证多线程并发时数据的访问安全

先来看一下Handler在new对象时所调用的构造方法:

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

大家应该都清楚,handler如果要发挥作用,那么looper是必不可少的,所以在handler创建时,就会通过Looper的静态方法来创建一个looer来和hander一起工作。那么继续看这个Looper.myLooper()的静态方法内部的实现:

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

可以看到他返回的是一个sThreadLocal.get()方法,OK,下面是这个get()方法的代码块:

 public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}

private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

这个源码应该比较简单啊,就是通过获取到当前线程的对象,来从一个map中去拿value,然后给他返回。这一块比较好理解,那么就有一个问题出来了,这个存储数据的map是什么时候添加了数据的呢?

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

这段代码不知道大家有没有看过,Looper.prepare()方法不用我说大家应该都懂它的重要性,不管是主线程中系统底层自动去运行,还是子线程中我们去手动添加运行,反正在线程中使用handler,这个方法都是前提条件,这个大家应该明白的。那么很简单的就可以看出来,这个代码块仅仅是去给ThreadLocal给set了一个Looper对象。我们继续看

    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.prepare()方法执行的当前线程,来存储一个Looper对象,这样就使得Handler在new的时候能够获取到的就是关联了当前线程的一个looper。而且,因为使用的是map存储,所以只会存在一个当前线程的value,后面会把前面覆盖,就这么简单就实现了一个Looper、ThreadLocal以及Handler之间的关联。并且应该明白了如果你在子线程中使用Handler时的写法为什么会是下面这样了:
 
  *  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();
  *      }
  *  }

通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,因此取出来的是各自自己线程中的对象,这样ThreadLocal就使得各线程能够保持各自独立的一个对象,而不会发生数据错乱的情况。

猜你喜欢

转载自blog.csdn.net/m0_37194191/article/details/72801771