源码分析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就使得各线程能够保持各自独立的一个对象,而不会发生数据错乱的情况。