我对ThreadLocal的理解
声明:小弟菜狗一个。对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的理解的更多相关文章
- ThreadLocal深入理解二
转载:http://doc00.com/doc/101101jf6 今天在看之前转载的博客:ThreadLocal的内部实现原理.突然有个疑问, 按照threadLocal的原理, 当把一个对象存入到 ...
- ThreadLocal深入理解一
转载:http://www.cnblogs.com/dolphin0520/p/3920407.html 想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使 ...
- Java中的ThreadLocal深入理解
提到ThreadLocal,有些Android或者Java程序员可能有所陌生,可能会提出种种问题,它是做什么的,是不是和线程有关,怎么使用呢?等等问题,本文将总结一下我对ThreadLocal的理解和 ...
- ThreadLocal的理解与应用场景分析
对于Java ThreadLocal的理解与应用场景分析 一.对ThreadLocal理解 ThreadLocal提供一个方便的方式,可以根据不同的线程存放一些不同的特征属性,可以方便的在线程中进行存 ...
- Python中ThreadLocal的理解与使用
一.对 ThreadLocal 的理解 ThreadLocal,有的人叫它线程本地变量,也有的人叫它线程本地存储,其实意思一样. ThreadLocal 在每一个变量中都会创建一个副本,每个线程都可以 ...
- java中threadlocal的理解
[TOC] #java中threadlocal的理解##一.threadlocal的生命周期和ThreadLocalMap的生命周期可以吧TreadLocal看做是一个map来使用,只不过这个map是 ...
- ThreadLocal简单理解
在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...
- threadlocal彻底理解,深刻
本文转自http://blog.csdn.net/huachao1001/article/details/51970237 ThreadLocal的使用相信大家都比较熟悉,但是ThreadLocal内 ...
- ThreadLocal深入理解与内存泄露分析
ThreadLocal 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本.所以每个线程都能够独立地改变自己的副本.而不会影响其他线程所相应的副本. ...
随机推荐
- SQL Sever语言 事务
事务:保障流程的完整执行保证程序某些程序在运行时同时成功同时失败,保证程序的安全性 begin tran --在流程开始的位置加 --此处写SQL语句 if @@error>0 --ERRORS ...
- 【Spring】AOP
AOP 编程允许你把遍布应用各处的功能分离出来形成可重用的组件,将安全.事务和日志关注点与你的核心业务逻辑相分离. 面向切面编程往往被定义为促使应用程序分离关注点的一项技术.系统由许多不同组件组成,每 ...
- [ BZOJ 3038 & 3211 / SPOJ GSS4 ] 上帝造题七分钟2 / 花神游历各国
\(\\\) \(Description\) 给出一个长度为\(N\)的数列,共进行\(M\)次操作: \(1\ L\ R\):查询\([L,R]\)区间和. \(2\ L\ R\):对\([L,R] ...
- PHP开发之旅-表单验证
一.创建表单 <form name = "login" method = "post" action="contact.php?action=l ...
- Win32双缓冲讲解
双缓冲是一种思想,也是一种方法,它可以避免频繁的闪烁问题.如果在画布上直接绘画,由于每次都会重新擦除然后重绘,绘制需要时间,所以肉眼会看到闪烁问题.解决的方法就是在内存中先创建出一个内存dc,然后在内 ...
- node 第三方包学习
时间格式化 moment var moment = require('moment'); moment().format();
- CSS——样式初始化
腾讯: body,ol,ul,h1,h2,h3,h4,h5,h6,p,th,td,dl,dd,form,fieldset,legend,input,textarea,select{margin:0;p ...
- JS——switch case
语法: switch(n) { case 1: 执行代码块 1 break; case 2: 执行代码块 2 break; default: n 与 case 1 和 case 2 不同时执行的代码 ...
- [Windows Server 2008] 查看PHP详细错误信息
★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com ★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频. ★ 本节我们将带领大家:查看IIS ...
- c/c++排坑(3) -- c/c++中的switch语句
switch语句的简单介绍 一个 switch 语句允许测试一个变量等于多个值时的情况.每个值称为一个 case,且被测试的变量会对每个 switch case 进行检查. switch(expres ...