转载请标明出处:【顾林海的博客】
个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!
##前言
很长时间没写博客,不是自己懒,而是在这段时间在思考要写什么,以及自己的发展方向,之前的自己很浮躁,总想表现什么,这其实对技术人来说是不好的,把心沉淀下来,找准方向,再一步步去实现,幸运的是自己又找到自我,这篇文章就讲讲一个小知识点ThreadLocal。
ThreadLocal与多线程并发没有任何关系,ThreadLocal解决的是线程读写各自内部对象的问题,而多线程并发是指临界资源被多个线程访问从而导致数据的一致性
的问题。
提供两个对外的方法:get()和set()方法用于内部对象的读写操作。
mBooleanThreadLocal.set(true);
Log.d(TAG, "[Thread#main]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
new Thread("Thread#1") {
@Override
public void run() {
mBooleanThreadLocal.set(false);
Log.d(TAG, "[Thread#1]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
};
}.start();
new Thread("Thread#2") {
@Override
public void run() {
Log.d(TAG, "[Thread#2]mBooleanThreadLocal=" + mBooleanThreadLocal.get());
};
}.start();
在主线程中输出true,在Thread#1中输出false,在Thread#2中输出null。
总结,不同线程访问同一个ThreadLocal对象,ThreadLocal对象维护一套数据的副本并且彼此互不干扰。
get() 方法源码如下:
public T get() {
//以当前线程作为getMap()方法的参数。
Thread t = Thread.currentThread();
//获取当前线程保存ThreadLocal值的ThreadLocalMap对象。
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
//返回当前线程的ThreadLocalMap对象。
return t.threadLocals;
}
上面代码中get()方法有三个个分支路线可走:
如果能直接获取值当然好了,但获取不到值就只能执行setInitialValue()方法:
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
//获取到当前线程的ThreadLocalMap,从中获取值,如果获取的值不存在。
map.set(this, value);
else
//获取到当前线程的ThreadLocalMap,发现ThreadLocalMap对象为空
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
分析上面三个分支路线的后两种:
ThreadLocalMap对象存在,但获取不到值时,setInitialValue()方法就会执行map.set(this,value)方法用于初始化,最终会返回null
ThreadLocalMap对象都不存在,会间接的执行createMap(t,value)方法,用于创建ThreadLocalMap对象,如下:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
值得注意的是上面的initialValue()方法是protected类型的,也就是说开发人员可以在创建ThreadLocal时重写initialValue方法,以满足特定
的业务需求。
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程保存ThreadLocal值的ThreadLocalMap对象。
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
根据当前线程获取的ThreadLocalMap对象,当ThreadLocalMap对象存在就保存value,否则创建ThreadLocalMap对象并保存value。
在Android的Handler机制中Looper类使用ThreadLocal保存对应线程的Looper实例。
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();
}
}
工作线程中使用Looper之前必须调用prepare()方法初始化。
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//保存当前线程的Looper实例
sThreadLocal.set(new Looper(quitAllowed));
}
prepare()就保存了每个工作线程的Looper实例,这样Handler内部就能获取到当前线程的Looper并发送消息。
static class ThreadLocalMap {
static class Entry extends WeakReference> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}
省略相关代码
}
ThreadLocalMap 在内部实现时对于 ThreadLocal 中对象的引用使用的是弱引用类型,从而规避内存泄漏的风险。