Handler系列——线程同步问题(二)

如果对handler了解不多先看上一篇handler基础:
Handler又是什么鬼东西

1. Hadler的作用

handler是用于线程间通信的,但是它并不是仅仅用于处理UI界面,而handler更多的是整个app通信的框架。Android是基于事件驱动的,即所有的activity生命周期都是通过handler事件驱动的。
既然handler这么重要,那它的线程安全就至关重要,关系着整个app的运行,那么它是如何保证自己的线程安全的呢?

2. MessageQueue消息管理

Handler机制里最主要的类MessageQueue,这个类就算所有消息的存储仓库,在这个仓库中,同时插入和取出消息,我们如何管理好消息,就是handler线程安全的关键点了
消息管理主要包括两点:

  1. 消息入库(enqueueMessage)
  2. 消息出库(next)

所以这两个接口是确保线程安全的主要档口。

3. enqueueMessage源码

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");
			Log.w(TAG, e.getMessage(), e);
			msg.recycle();
			return false;
		}
		msg.markInUse();
		msg.when = when;// when执行的时间点
		Message p = mMessages;// mMessages队列头部的msg
		boolean needWake;// 是否需要唤醒阻塞
		if (p == null || when == 0 || when < p.when) {
			// 当没有消息 || 不延迟执行  || 时间小于队列头部消息的执行时间的时候,直接操作链表,把消息放到队列最前面
			// New head, wake up the event queue if blocked.
			msg.next = p;
			mMessages = msg;
			needWake = mBlocked;//注意此处,如果当前的队列是出于阻塞的状态,那么mBlocked就会为true
		} else {
			// Inserted within the middle of the queue. Usually we don't have to wake
			// up the event queue unless there is a barrier at the head of the queue
			// and the message is the earliest asynchronous message in the queue.
			needWake = mBlocked && p.target == null && msg.isAsynchronous();
			Message prev;
			// 循环消息队列,当到队尾或者当前消息的执行时间 < 下一个消息的执行时间的时候,将当前消息插入链表
			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);// 调用native方法,唤醒阻塞;
		}
	}
}
//锁结束的地方

synchronized锁是一个内置锁,也就是由系统控制锁的lock unlock时机的。this也就是MessageQueue

synchronized(this)

这个锁说明的是,对所有调用同一个MessageQueue对象的线程来说,他们都是互斥的;
在我们Handler中,一个线程对应一个唯一的Looper对象,而Looper中又只有一个唯一的MessageQueue,所以我们的主线程中就只有一个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候,主线程一次都只会处理一个消息,其他的都需要等待,这样就不会出现消息队列混乱的问题

4. next()源码

Message next() {
	....
	for (;;) {
	....
	nativePollOnce(ptr, nextPollTimeoutMillis);
	synchronized (this) {
		// Try to retrieve the next message. Return if found.
		...
		return msg;
		}
		} else {
			// No more messages.
			nextPollTimeoutMillis = -1;
		}
	...
	}//synchronized 结束之处
	// Run the idle handlers.
	// We only ever reach this code block during the first iteration.
	for (int i = 0; i < pendingIdleHandlerCount; i++) {
		final IdleHandler idler = mPendingIdleHandlers[i];
		mPendingIdleHandlers[i] = null; // release the reference to the handler
		boolean keep = false;
		try {
			keep = idler.queueIdle();
		} catch (Throwable t) {
			Log.wtf(TAG, "IdleHandler threw exception", t);
		}
		if (!keep) {
			synchronized (this) {
				mIdleHandlers.remove(idler);
			}
		}
	}
	// Reset the idle handler count to 0 so we do not run them again.
	pendingIdleHandlerCount = 0;
	// While calling an idle handler, a new message could have been delivered
	// so go back and look again for a pending message without waiting.
	nextPollTimeoutMillis = 0;
	}
}

看源码发现,从线程中取消息,每次都是队列的头部取,那么它加锁是不是没有意义呢?答案当时是否定的。
我们必须在next里加锁,因为由于synchronized(this)作用范围是所有this正在访问的代码块都会有保护作用,也就是它可以保证next函数和enqueueMessage函数能够实现互斥,这样才能真正的保证多线程访问的时候,messageQueue可以有序的进行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值