ThreadLocal工作原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定的线程中可以获取到存储的数据,对于其他线程来说则无法获取到数据
当某些数据以线程为作用域并且不同线程具体不同的数据副本,可以考虑使用ThreadLocal
6.0以前实现
set()
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
首先拿到当前线程localValues,如果localValues没有值,就会初始化一个Values;
Values中有一个Object[],其实这个数组是一个以key-value的方式来存放threadLocal.set()进来的值;
在数组中key对应的是threadLocal.reference,key的index是通过 threadLocal.hash & threadLocal.mask 计算出来的值
在数组中value对应的就是ThreadLocal.set()进来的值,value的index就是key的index的下一位即index+1
key的index元素说明:reference、hash & mask
reference = new WeakReference<ThreadLocal<T>>(this);this-->ThreadLocal
hash = hashCounter.getAndAdd(0x61c88647 * 2);
hashCounter = new AtomicInteger(0);原子操作Integer的类,高并发安全
mask = Object[].length-1
get()
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
7.0以后的实现
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);
}
与之前不同的就是Value实现改为了ThreadLocalMap,将之前的thread.localValues改成了threadLocals;
首先拿到当前线程threadLocals,如果localValues没有值,就会初始化一个ThreadLocalMap;
ThreadLocalMap中有一个Entry[],这个Entry是一个以key-value的方式来存放threadLocal.set()进来的值;
Entry继承自WeakReference<ThreadLocal>
key存放的就是ThreadLocal,内部还是存放了一个weakReference,泛型为ThreadLocal
value存放的就是set进来的值
entry的index还是通过 threadLocal.hash & threadLocal.mask 计算出来的
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();
}
MessageQueue 工作原理
- 通过一个单链表的数据结构来维护消息列表,单链表插入删除操作有优势;
- enqueueMessage()就是往单链表中插入数据
- next()是一个无限循环的方法,如果消息队列中没有消息一直阻塞,当有新消息到来时,next方法返回这条消息并将其从单链表中移除
Looper 工作原理
-
不停地从MessageQueue中查看是否有新消息,有的话立刻处理,否则一直阻塞
-
可以通过Looper.prepare()为当前线程创建一个looper,通过loop方法开启消息轮询
-
Looper也是可以退出的,提供了quit()和quitSafely();
- 二者区别
- quit直接退出
- quitSafely设定了一个退出标记,然后把消息队列中已有的消息处理完毕退出
Looper退出以后,handler会发送失败,send()返回false
-
loop()方法是一个死循环,唯一跳出循环的方式就是MessageQueue.next()方法返回null
-
当looper的quit方法被调用的时候就会调用MessageQueue的quit或者quitSafely方法通知消息队列退出,否则loop()方法就会无限循环下去
-
当没有消息时,next方法一直阻塞,导致loop方法阻塞
-
如果next方法返回了消息,Looper就会处理,msg.target.dispatchMessage(msg)
Handler 工作原理
- Handler发送消息的过程就是向消息队列中插入了一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息就开始处理,最终由Looper将消息交给Handler处理,即Handler的dispatchMessage方法
- 检查Message的callBack是否为null,不为null就通过handleCallback来处理消息,Message的callBack是一个Runable对象,实际上就是handler的post方法所传递的Runnable参数
- 其次检查mCallback是否为null,不为null就调用mCallback的handleMessage来处理消息,Callback是个接口
- 最后会执行handler的handleMessage()
为什么主线程不会因为Looper.loop()里的死循环卡死?
进入死循环之前便创建了新Binder线程,创建一个Binder线程(具体是指ApplicationThread,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message发送给主线程
ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中执行,这个过程就是主线程的消息循环模型