声明:小弟菜狗一个。对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);
else
createMap(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() {
...
// 获取looper
mLooper = 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(new
LogPrinter(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、调用prepare
prepare();
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;
// 往队列里面加入Message
sent = 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唤醒状态置true
msg.next = p;
mMessages = msg;
needWake = mBlocked; // new head, might need to wake up
} else {
// 当前发送的message被排队到其它message的后面。needWake唤醒状态置false
Message 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);
}
}

我对ThreadLocal的理解的更多相关文章

  1. ThreadLocal深入理解二

    转载:http://doc00.com/doc/101101jf6 今天在看之前转载的博客:ThreadLocal的内部实现原理.突然有个疑问, 按照threadLocal的原理, 当把一个对象存入到 ...

  2. ThreadLocal深入理解一

    转载:http://www.cnblogs.com/dolphin0520/p/3920407.html 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使 ...

  3. Java中的ThreadLocal深入理解

    提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...

  4. ThreadLocal的理解与应用场景分析

    对于Java ThreadLocal的理解与应用场景分析 一.对ThreadLocal理解 ThreadLocal提供一个方便的方式,可以根据不同的线程存放一些不同的特征属性,可以方便的在线程中进行存 ...

  5. Python中ThreadLocal的理解与使用

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

  6. java中threadlocal的理解

    [TOC] #java中threadlocal的理解##一.threadlocal的生命周期和ThreadLocalMap的生命周期可以吧TreadLocal看做是一个map来使用,只不过这个map是 ...

  7. ThreadLocal简单理解

    在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...

  8. threadlocal彻底理解,深刻

    本文转自http://blog.csdn.net/huachao1001/article/details/51970237 ThreadLocal的使用相信大家都比较熟悉,但是ThreadLocal内 ...

  9. ThreadLocal深入理解与内存泄露分析

    ThreadLocal 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本.所以每个线程都能够独立地改变自己的副本.而不会影响其他线程所相应的副本. ...

随机推荐

  1. Html基础学习(基于W3school网络教程)

    //防止乱码 head中加入 <meta http-equiv="Content-Type" content="text/html; charset=utf-8&q ...

  2. Python--10、进程知识补充

    守护进程 基于进程启动的子进程,会和主进程一起结束.主进程结束的依据是程序的代码执行完毕. #创建守护进程p=Process(task) p.daemon = True p.start() 子进程需要 ...

  3. vs for Mac中的启用Entity Framework Core .NET命令行工具

    在vs for Mac的工具菜单中已没有了Package Manager Console. 我们可以通过以下方法使用Entity Framework Core .NET命令行工具: 1.添加Nuget ...

  4. OpenCV:使用OpenCV3随机森林进行统计特征多类分析

    原文链接:在opencv3中的机器学习算法练习:对OCR进行分类 本文贴出的代码为自己的训练集所用,作为参考.可运行demo程序请拜访原作者. CNN作为图像识别和检测器,在分析物体结构分布的多类识别 ...

  5. LVS部分调度算法的适应场景分析

    1.轮叫调度算法(RR)假设所有服务器处理性能均相同,不管服务器的当前连接数和响应速度.该算法相对简单,不适用于服务器组中处理性能不一的情况,而且当请求服务时间变化比较大时,轮叫调度算法容易导致服务器 ...

  6. Makefile精髓篇【转】

    什么是makefile?或许非常多Winodws的程序猿都不知道这个东西,由于那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序猿,makefile还是 ...

  7. PHP 之ip查询接口

    /** * @param $ip 待查询的ip * @return mixed */ function getIpAddressInfo($ip) { $ipurl = 'http://api.ip1 ...

  8. Vue项目优化首屏加载速度

    Vue项目部署上线后经常会发现首屏加载的速度特别慢:那么有那写能做的简单优化呢 一.路由的懒加载 路由懒加载也就是 把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件. 结合 ...

  9. cad二次开发中各种头的定义

    Database db=HostApplicationServices.WrokingDatabase; Editor ed=Autodesk.AutoCAD.ApplicationService.A ...

  10. linux 的sed命令解释 sed ':t;N;s/\n/,/;b t' 将换行符换成逗号

    linux 的sed命令解释 sed ':t;N;s/\n/,/;b t' 将换行符换成逗号 实现的功能是吧换行符换成逗号了,自己试验过. 求解释,:t N b t 都是什么意思??? :t 定义la ...