声明:小弟菜狗一个。对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. Java系列学习(十二)-开始Eclipse

    1.用Eclipse来写一个HelloWorld (1)选择工作空间 工作空间其实就是我们写的源代码所在的目录 (2)创建一个Java项目 [File-New-Java Project] (3)创建包 ...

  2. ubuntu 安装redis以及phpredis

    一.安装redis 1. 去百度搜索 redis,然后去靠谱的地方下载最新的redisxxx.tar.gz 2. 解压后,sudo make 3. sudo make install 4. //安装完 ...

  3. 常用的Axure操作方法(1)

    1. 保存原型图片到本地,如在网页上看到图标素材,好多个在一张图上.                                   如上图所示,将图片拖入axure中,利用分割或者裁剪,把小图标 ...

  4. Elasticsearch_Lucene基础

    Lucene基本概念 文档(document):索引与搜索的主要载体,它包含一个或多个字段,存放将要写入索引的或将从索引搜索出来的数据. 字段(field):文档的一个片段,它包含字段的名称和字段的内 ...

  5. Mac下CUDA开启及Tensorflow-gpu 1.4 安装

    本文由@ray 出品,转载请注明出处.  文章链接:http://www.cnblogs.com/wolfray/p/8040694.html 在之前的文章中,笔者介绍了在Mac下安装Tensorfl ...

  6. 如何修改wampserver中mysql中字符编码的解决方案

    因为我用的一般都是utf8,所以有必要改一下: 打开mysql控制台,输入密码登录之后,执行命令: show variables like ‘%char%’; 注意引号的中英文格式以及最后面的分号不要 ...

  7. JS——offset

    1.offsetWidth.offsetHeight返回盒子宽度和高度,包括padding与border,不包括margin 2.offsetLeft.offsetTop返回盒子距离定位盒子的x轴方向 ...

  8. JS——冒泡排序

    核心思想: 1.外层for循环控制比较的轮数 2.内层for循环控制每轮比较的次数 3.外层每进行一轮比较,内层就少一次比较,因为外层每进行一轮比较都会产生一个最大值 <script> v ...

  9. java 中String与StringBuilder 效率

    之前印象中string与stringbuilder操作时,如果多次改变string就使用stringbuilder,效率会提高: 今天实际遇到了问题,亲身经历过之后,这性能不是一般的影响啊:不是同一个 ...

  10. Java 基础入门随笔(1) JavaSE版——java语言三种技术架构

    1.java语言的三种技术架构: J2SE(java 2 Platform Standard Edition):标准版,是为开发普通桌面和商务应用程序提供的解决方案.该技术体系是其他两者的基础,可以完 ...