声明:小弟菜狗一个。对ThreadLocal的描写叙述和理解难免有所偏差

近期由于须要深入的了解android的handler消息机制而去查看了Looper的源代码。众所周知在主线程中是不须要在程序猿在代码新建一个Looper对象的,由于在主线程创建时它就被创建出来了。所以就好奇它是怎么被创建出来的然后发现它跟ThreadLocal 有关于是便查看了该类的一些资料,但还是不太理解。于是便尝试自己去看一下源代码,然后就有了对ThreadLocal一个又一次的认识。先贴出Looper的代码:

   private Looper() {//MessageQueue对象会随Looper对象的创建而创建mQueue = new MessageQueue();mRun = true;mThread = Thread.currentThread();}

以下是Looper的代码,从代码中看出sThreadLocal是Looper的成员变量。它被new出来了。

当我第一次看到此代码的时候便产生了一个疑问,印象中不是说ThreadLocal对象都会绑定到一个线程中去的吗,若创建对象那么怎样确定它绑定到哪一个线程中呢(到后来我发现我的这样的想法是不正确的)。于是我便查看了ThreadLocal的代码。首先由于prepare调用到ThreadLocal的set方法。以下先查看下该方法的实现

public class Looper {private static final boolean DEBUG = false;private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;// sThreadLocal.get() will return null unless you've called prepare().private static final ThreadLocal sThreadLocal = new ThreadLocal();<span style="font-family: Arial, Helvetica, sans-serif;">   </span>
//该方法事实上就是将一个Looper对象设置进ThreadLocal的一个map中
public static final void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper());}
public void set(T value) {
        Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);//ThreadLocalMap是ThreadLocal的内部类if (map != null)map.set(this, value);elsecreateMap(t, value);}

由ThreadLocal的方法不难看出set方法设置的值最后会与本ThreadLocal对象凑成一个键值对存放到它新建的ThreadLocalMap对象中的。

此时会注意到两个方法getMap(ThreadLocal tl,T t)和createMap(Thread t, T t)。

通过方法名就不难得出此双方法是跟ThreadLocalMap对象的获取和创建有关。

以下先观察ThreadLocal类中createMap方法的代码

 void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}

通过代码能够知道此方法将一个新建的“键值对”为本ThreadLocal对象和要设置的value值的ThreadLocalMap对象赋值给当前线程的threadLocals变量。接下来查看Thread的代码。

  /* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;

可见它是Thread的一个成员变量。至此当前线程的threadLocals就不为空并且是不会再被改变(由于从ThreadLocal的set方法中每一次在设置当前threadLocals的值之前都会先推断该对象是否为null)。

通过观察这一系列的代码能够了解到事实上在每个线程中都会有一个ThreadLocal.ThreadLocalMap变量,它与Map有点类似用于存放键值对,只是它的键是ThreadLocal对象,所以一个ThreadLocal对象仅仅能在它所在线程的ThreadLocal.ThreadLocalMap对象对象中存放有自己是key的一个值。事实上此时又会产生一个疑问这种以ThreadLocal

为key的键值对存放到Thread对象中的ThreadLocal.ThreadLocalMap中有什么意义呢?由于当我们失去了ThreadLocal对象之后就不能取出在线程中以该ThreadLocal的相应值。

事实上通过观察Looper的代码不难看出它的ThreadLocal sThreadLocal对象是一个静态变量。因此全部的Looper对象都在“共用”一个ThreadLocal 对象。因此确保了不同Looper的Looper.prepare方法在同一个线程的ThreadLocal.ThreadLocalMap中相应的值是一样的,这确保了一个线程中仅仅有一个Looper对象存放在当前线程的ThreadLocal.ThreadLocalMap中。

下面是Message、Message Queue、Handler、Looper类之间的大概的联系。

#### Handler消息机制

> #### Message 消息

Message msg = Message.obtain()Message msg = new Message()//获取Message类的两种方式

> #### Handler

new Handler(){
handlerMessage(Message msg){
// 处理消息
}
}

> #### Handler的构造方法:

public Handler() {...
// 获取loopermLooper = Looper.myLooper();//事实上就是在本线程的ThreadLocal.Map中取looper对象if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue = mLooper.mQueue;mCallback = null;}
   public static Looper myLooper() {return sThreadLocal.get();}

> #### 主线程设置Looper。在ActivityThread类里面

public static final void main(String[] args) {....
// 1.主线程创建Looper Looper.prepareMainLooper();if (sMainThreadHandler == null) {sMainThreadHandler = new Handler();}ActivityThread thread = new ActivityThread();thread.attach(false);if (false) {Looper.myLooper().setMessageLogging(newLogPrinter(Log.DEBUG, "ActivityThread"));}Looper.loop();

> #### Looper

public static final void prepare() {//若在调此方法时本线程(非主线程)中不存在looper对象则会创建一个looper对象存放在线程的ThreadLocalMap对象中if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}

// 3、在主线程中设置Looper, new Looper()里面创建了一个MessageQueue

        sThreadLocal.set(new Looper());public static final void prepareMainLooper() {// 2、调用prepareprepare();setMainLooper(myLooper());if (Process.supportsProcesses()) {myLooper().mQueue.mQuitAllowed = false;}}

> #### 主线程调用Looper.loop()方法,主线程就会堵塞,是一个死循环。使用管道(Pipe),是Linux中的一种进程间通信方式,使用了特殊的文件,有两个文件描写叙述符(一个是读取,一个是写入)

> #### 应用场景;主进程拿着读取描写叙述符等待读取,没有内容时就堵塞,还有一个进程拿写入描写叙述符去写内容,唤醒主进程,主进程拿着读取描写叙述符读取到内容。继续运行。

> #### Handler应用场景:Handler在主线程中创建,Looper会在死循环里等待取消息,1、没取到。就堵塞,2、一旦被子线程唤醒,取到消息。就把Message交给Handler处理。

子线程用Handler去发送消息。拿写入描写叙述符去写消息,唤醒主线程。

 public static final void loop() {...while (true) {
// 取消息。假设没有消息。就堵塞Message msg = queue.next(); // might block...msg.target.dispatchMessage(msg);...
}}

> #### Handler发送消息代码

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{....
// 把Message的target置为当前发送的Handler,以便Looper取到message后依据target把message分发给正确的Handler
msg.target = this;
// 往队列里面加入Messagesent = queue.enqueueMessage(msg, uptimeMillis);....}

> #### MessageQueue.enqueueMessage 代码

final boolean enqueueMessage(Message msg, long when) {...Message p = mMessages;if (p == null || when == 0 || when < p.when) {
// 当前发送的message须要立即被处理调。needWake唤醒状态置truemsg.next = p;mMessages = msg;needWake = mBlocked; // new head, might need to wake up} else {
// 当前发送的message被排队到其它message的后面。needWake唤醒状态置falseMessage prev = null;while (p != null && p.when <= when) {prev = p;p = p.next;}msg.next = prev.next;prev.next = msg;needWake = false; // still waiting on head, no need to wake up}}
// 是否唤醒主线程if (needWake) {nativeWake(mPtr);}return true;> #### Handler.dispatchMessage方法public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}
// 把Message交给Handler处理handleMessage(msg);}}

转载于:https://www.cnblogs.com/liguangsunls/p/6880337.html

我对ThreadLocal的理解相关推荐

  1. python中ThreadLocal的理解与使用

    一.对 ThreadLocal 的理解 ThreadLocal,有的人叫它线程本地变量,也有的人叫它线程本地存储,其实意思一样. ThreadLocal 在每一个变量中都会创建一个副本,每个线程都可以 ...

  2. ThreadLocal的理解

    ThreadLocal的理解     [文章出处]http://blog.csdn.net/lufeng20/article/details/24314381 (五星推荐)     [参考博客]htt ...

  3. 一.对ThreadLocal的理解

    一.对ThreadLocal的理解 ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多.可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个 ...

  4. 精选(26)面试官:讲讲你对ThreadLocal的理解

    前言 在面试环节中,考察"ThreadLocal"也是面试官的家常便饭,所以对它理解透彻,是非常有必要的. 有些面试官会开门见山的提问: "知道ThreadLocal吗? ...

  5. 面试官:听说你精通并发编程,来说说你对ThreadLocal的理解

    ThreadLocal 简介 ThreadLocal 是一个解决多线程并发问题的工具类,ThreadLocal有的人可能理解为本地线程,这个并不是正确的理解.「ThreadLocal并不是一个线程,应 ...

  6. 谈谈对ThreadLocal的理解?(基于jdk1.8)

    在java的多线程模块中,ThreadLocal是经常被提问到的一个知识点,提问的方式有很多种,可能是循序渐进也可能是就像我的题目那样,因此只有理解透彻了,不管怎么问,都能游刃有余. 这篇文章主要从以 ...

  7. ThreadLocal 的理解

    介绍 ThreadLocal类用来提供线程内部的局部变量.这种变是在多线程环境下访问(通过get和set方法访问)时能保证各个线程的变量相对独立于其他线程内的变量,ThreadLocal实例通常来说都 ...

  8. 多线程与ThreadLocal的理解笔记

    一.线程与进程的区别 进程是指一种"自包容"的运行程序,由操作系统直接管理,直接运行,有自己的地址空间,每个进程一开启都会消耗内存. 线程是进程内部单一的顺序控制流,一个进程拥有多 ...

  9. ThreadLocal原理分析与使用场景

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:阿凡卢 cnblogs.com/luxiaoxun/p/87 ...

最新文章

  1. go语言编程之字符串操作
  2. 发送文件到打印机,打印机收不到(无线打印机)
  3. Java日志框架-Spring中使用Logback(Spring/Spring MVC)
  4. CTFshow 命令执行 web33
  5. spring boot使用freemarker页面获取系统路径最简配置
  6. 【Java数据库】使用properties资源文件,简化数据库连接
  7. ES6结构赋值的用途
  8. Visual Studio 2008单元测试实践一
  9. 双柱状图柱子数量比较多_一条代码完成堆叠柱状图-冲击图的操作-终结版
  10. 关于团队开发项目的想法
  11. 交互式内核图 -***
  12. python03_Python中的列表、元组、字典和集合
  13. 人工智能之语音识别技术【科普】
  14. Ps 初学者教程,如何用文字增强您的照片?
  15. 极客大学架构师训练营 架构师职责 听课总结 -- 第一课
  16. 微软王码五笔86版 for win95/98/me 官网
  17. 区分LJMP、AJMP、SJMP、JMP指令
  18. 车金融|合同中心系统的前世今生
  19. waitpid status参数介绍
  20. 被称为偏执的企业家,他成功跻身中国民企500强

热门文章

  1. C语言经典算法100例-031-判断星期几
  2. HTML/CSS/Javascript代码在线压缩、格式化(美化)工具
  3. alpine_glibc 构建sun jdk 8的docker镜像
  4. boltdb 学习和实践
  5. 第一个冲刺周期-第三天
  6. QQuickRenderControl
  7. 8大排序算法图文讲解转
  8. Flex与javascript通信
  9. java数组与C++数组间的区别
  10. 增量导出_[华为]一种实用的增量式深度CTR模型训练方法