我对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为每个使用该变量的线程提供独立的变量副本.所以每个线程都能够独立地改变自己的副本.而不会影响其他线程所相应的副本. ...
随机推荐
- SVN系列学习(三)-TortoiseSVN的基本操作
1.添加(Add) 在ZJHZXS_01中,新建一个记事本,在记事本中写上一下内容,然后保存,再打开,再保存 这个时候,在选中文件夹ZJHZXS_01,并右击[SVN Commit] 提交成功,加了一 ...
- 使用Dreamweaver在一张图片上添加多个热点链接
所有代码: <html> <head> <meta charset="utf-8"> <title>无标题文档</title& ...
- C# WinForm窗体应用(第四天)
一.点击登录按钮,将两个窗体进行连接,并进行用户名和密码验证. /// <summary> /// 登录设置 /// </summary> /// <param name ...
- TriAquae 是一款由国产的基于Python开发的开源批量部署管理工具
怀着鸡动的心情跟大家介绍一款国产开源运维软件TriAquae,轻松帮你搞定大部分运维工作!TriAquae 是一款由国产的基于Python开发的开源批量部署管理工具,可以允许用户通过一台控制端管理上千 ...
- groupbox
使用groupbox将radiobox 放入其中可以使组框中只选中一个
- Typescript编译设置
TypeScript MSBuild编译选项,用记事本打开工程文件,进行修改,如<TypeScriptGeneratesDeclarations>true</TypeScriptGe ...
- 数据库操作(二)SOQL
1.SOQL SOQL是对象查询语言.它可以在单个sObject中在给定标准上搜索记录. 2.SELECT语句 [格式]SELECT 列名称 FROM 表名称 [示例] 3.SELECT...WHER ...
- Oracle行转列/列转行
1.oracle的pivot函数 原表 使用pivot函数: with temp as(select '四川省' nation ,'成都市' city,'第一' ranking from dual u ...
- Web 常用
System.Web.Hosting.HostingEnvironment.MapPath(); HttpUtility.UrlEncode();
- chrony配置介绍
rhel7 文档https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System_Adminis ...