Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue与Looper的支撑。Handler的主要任务是将一个任务切换到某个指定的线程中去执行。
Messageueue的中文翻译消息队列,顾名思义,它的内部存储了一组信息,一队列的形式对外提供插入和删除的工作。虽然叫做消息队列,但是它的内部存储结婚并不是真正的队列,而是采用单链表的数据结构来存储消息列表。
Looper的中文翻译为循环,而在这里可以理解为消息循环。Looper会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待。Looper中还有一个特殊的概念,就是ThreadLocal,ThreadLocal并不是线程。它的作用是可以在每个线程中存储数据。ThreadLocal可以在不同线程中互不干扰的存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。当然,线程是没有Looper的,如果需要使用Handler就必须为线程创建Looper。
我们经常提到的主线程,也叫UI线程,它就是ActivityThread,它被创建时就会初始化Looper,这也是主线程中默认可以使用Handler的原因。
提供Handler原因:
因为Android规定访问UI只能在主线程中进行,如果子线程中访问UI,那么程序就会抛出异常。但是Android又建议不要在主线程中执行耗时操作,否则会导致程序无法响应ANR。因此handler就是为了解决子线程中无法访问UI的矛盾。
而系统又为啥不允许在子线程中访问UI?
这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么系统不对UI控件的访问加上锁机制呢?缺点有两个:首先加上锁机制会让UI访问的逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。
ThreadLocal
工作原理
ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定的线程中可以获取到存储的数据,对于其他线程中来说则无法获取到数据。
使用场景
当某些数据是以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal。
ThreadLocal的另一个使用场景是复杂逻辑下的对象传递。
例子:
privateThreadLocalmBooleanThreadLocal=newThreadLocal();
得到:
其中,ThreadLocal的set方法:
线程隔离的秘密,就在于ThreadLocalMap这个类。ThreadLocalMap是ThreadLocal类的一个静态内部类,它实现了键值对的设置和获取(对比Map对象来理解),每个线程中都有一个独立的ThreadLocalMap副本,它所存储的值,只能被当前线程读取和修改。ThreadLocal类通过操作每一个线程特有的ThreadLocalMap副本,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。还有一点就是,ThreadLocalMap存储的键值对中的键是this对象指向的ThreadLocal对象,而值就是你所设置的对象了。
对于getMap和createMap方法的源码如下:
接下来看其get方法:
其中可看setInitialValue方法:
获取和当前线程绑定的值时,ThreadLocalMap对象是以this指向的ThreadLocal对象为键进行查找的,这当然和前面set()方法的代码是相呼应的。
进一步地,我们可以创建不同的ThreadLocal实例来实现多个变量在不同线程间的访问隔离,为什么可以这么做?因为不同的ThreadLocal对象作为不同键,当然也可以在线程的ThreadLocalMap对象中设置不同的值了。通过ThreadLocal对象,在多线程中共享一个值和多个值的区别,就像你在一个HashMap对象中存储一个键值对和多个键值对一样,仅此而已。
网友评论