Handler 源码分析


一、ThreadLocal是什么

ThreadLocal 是线程的局部变量, 是每一个线程所单独持有的,其他线程不能对其进行访问,ThreadLocal可以让每个线程拥有一个属于自己的变量的副本,不会和其他线程的变量副本冲突,实现了线程的数据隔离。每个线程都有一个ThreadLocalMap类型的threadLocals变量,ThreadLocalMap是一个自定义哈希映射,仅用于维护线程本地变量值。ThreadLocalMap是ThreadLocal的内部类,主要有一个Entry数组,Entry的key为ThreadLocal,value为ThreadLocal对应的值。

也就是说ThreadLocal本身并不真正存储线程的变量值,它只是一个工具,用来维护Thread内部的Map,帮助存和取。注意上图的虚线,它代表一个弱引用类型,而弱引用的生命周期只能存活到下次GC前。

二、ThreadLocal是什么ThreadLocal为什么会内存泄漏

ThreadLocal在ThreadLocalMap中是以一个弱引用身份被Entry中的Key引用的,因此如果ThreadLocal没有外部强引用来引用它,那么ThreadLocal会在下次JVM垃圾收集时被回收。这个时候就会出现Entry中Key已经被回收,出现一个null Key的情况,外部读取ThreadLocalMap中的元素是无法通过null Key来找到Value的。因此如果当前线程的生命周期很长,一直存在,那么其内部的ThreadLocalMap对象也一直生存下来,这些null key就存在一条强引用链的关系一直存在:Thread --> ThreadLocalMap-->Entry-->Value,这条强引用链会导致Entry不会回收,Value也不会回收,但Entry中的Key却已经被回收的情况,造成内存泄漏。

但是JVM团队已经考虑到这样的情况,并做了一些措施来保证ThreadLocal尽量不会内存泄漏:在ThreadLocal的get()、set()、remove()方法调用的时候会清除掉线程ThreadLocalMap中所有Entry中Key为null的Value,并将整个Entry设置为null,利于下次内存回收。

三、Handler机制

我们主要看一下Looper源码, Looper 在程序启动的时候系统就已经帮我们创建好了

在main方法中系统调用了 Looper.prepareMainLooper();来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环。来看看Looper.prepareMainLooper()是怎么创建出这两个对象的

//系统实例化 Handler
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

可以看到,在这个方法中调用了 prepare(false);方法和 myLooper();方法,那么再进入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));
}

在这里可以看出,sThreadLocal对象保存了一个Looper对象,首先判断是否已经存在Looper对象了,以防止被调用两次。sThreadLocal对象是ThreadLocal类型,因此保证了每个线程中只有一个Looper对象。

// Looper 在实例化的时候也实例化了一个消息队列同时还持有了当前线程的引用
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

//然后我们从发送消息查看源码
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}

------经过几个方法的调用进入下面的方法

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
// mQueue 在 Handler 实例化的时候就从当前线程中取出消息队列并赋值了
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(this + "sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 重点在这里,把当前 Handler 的引用赋值给 msg 的 target
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

------进入消息队列的源码

boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}

synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// p == null 代表前面没有消息, when 是延迟消息的时间值
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//当前消息的 next 是 p 引用,形成一个单链表结构,如果是第一个消息的话,p 为空
msg.next = p;
// 赋值消息到轮询器
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 发送了第一个消息后
mMessages 就不为空了
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

0 个评论

要回复文章请先登录注册