三. 线程管理之ThreadLocal
不忘初心 砥砺前行, Tomorrow Is Another Day !
相关文章
- 一. 线程管理之Thread基础
- 二. 线程管理之线程池
- 三. 线程管理之ThreadLocal
- 四. 线程管理之Android中的多线程
本文概要:
- 认识ThreadLocal
- 了解ThreadLocal的实现原理
在Android系统源码中,多处用到了Threadlocal,如最熟悉的Handler中的Looper,其次还有属性动画中AnimationHandler、ActivityThread、AMS都有涉及到.接下来一起来认识与了解它.
一. 认识ThreadLocal
概念:线程内部的数据存储类,可以实现在不同线程具有不同数据副本.它的作用域仅限于当前当前线程,线程之间互不干扰.
基本使用
这里定义了一个ThreadLocal用来存储Integer类型的数据,设置了默认值为999.分别在MainThread,SubThreadA对ThreadLocal的值进行设置,SubThreadB未进行设置值.
示例源码
public class ThreadLocalActivity extends AppCompatActivity {private static final String TAG = "ThreadLocalActivity";private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){@Overrideprotected Integer initialValue() {return 999;}};public static void startActivity(Context pkgContext) {Intent intent = new Intent(pkgContext, ThreadLocalActivity.class);pkgContext.startActivity(intent);}@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_threadlocal);threadLocal.set(100);new SubThreadA("SubThreadA").start();new SubThreadB("SubThreadB").start();System.out.println("MainThread:" + threadLocal.get());}class SubThreadA extends Thread {public SubThreadA(String name) {super(name);}@Overridepublic void run() {threadLocal.set(110);int value = threadLocal.get();System.out.println("SubThreadA:" + value);}}class SubThreadB extends Thread {public SubThreadB(String name) {super(name);}@Overridepublic void run() {int value = threadLocal.get();System.out.println("SubThreadB:" + value);}}}//调用输出
I: SubThreadA:110
I: MainThread:100
I: SubThreadB:999复制代码
最后通过调用输出结果,可以清晰的看到虽然操作的是同一个ThreadLocal对象,但是在三个不同线程存储的值是互不影响的.回过头再对照看概念性内容就清晰明了许多.另外在SubThreadB因为没有设置值,所以获得的是我们初始化设置的默认值999.
二. 了解ThreadLocal的实现原理
认识了它基本使用,接着一起来了解ThreadLocal的基本原理,之所以用了解,因为笔者自己对ThreadLocal的理解有限且实际开发中运用较少.这里就简单的看下原理.
2.1 当调用Set方法进行存储值时.
对应源码
public void set(T value) {Thread t = Thread.currentThread();//获取当前线程的ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null)//不为空,直接存储值map.set(this, value);else//为空,创建一个ThreadLocalMap并存储值.createMap(t, value);}
复制代码
从上面源码可以看出,我们设置的值,是存储在ThreadLocalMap下的.当第一次存储时,先会createMap初始化一个ThreadLocalMap.接着看如何进行初始化的.
对应源码
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//将新值存储在table数组中,该数组类型是Entry,扩展了弱引用封装了ThreadLocal与value.table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);
}
复制代码
通过以上源码,可以很清楚的发现最终我们设置的值,是存储在一个Entry类型的table数组中的,这个Entry封装了ThreadLocal与Value.
接着,如果不是第一次set值,那么会进入ThreadLocalMap的set方法,我们继续往下看.
private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {//替换存在的值e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}//将值存储在table数组中tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}
复制代码
这里不分析具体算法,从上面可以发现最终也是保存在table数组中.
2.2 当调用Get方法获取存储值时.
对应源码
public T get() {Thread t = Thread.currentThread();//同样获取当前线程的ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null) {//不为空,获取对应值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//为空,返回设置的默认值return setInitialValue();
}
复制代码
通过以上源码,get方法简单来说就是从当前线程的ThreadLocalMap对象中获取存储的值.
通过SET与GET源码简单分析,我们知道它们所操作的对象都是当前线程的ThreadLolocalMap对象的table数组,因此在不同线程中访问同一个ThreadLocal的set和get方法,它们对ThreadLocal所做的读写操作仅限于各自线程的内部.
本文小结
最后我们对ThreadLocal的实现流程做一个小结.
- Set方法时,将值存储在当前线程的ThreadLolocalMap对象的table数组中.
- 该table数组是Entry类型,封装了ThreadLocal与Value.
- Get方法时,从当前线程的ThreadLolocalMap对象的table数组中获取存储的值.
最后奉上一个极简版的ThreadLocal的工作流程图.
由于本人技术有限,如有错误的地方,麻烦大家给我提出来,本人不胜感激,大家一起学习进步.
参考链接:
- www.cnblogs.com/whoislcj/p/…
三. 线程管理之ThreadLocal相关推荐
- 二. 线程管理之线程池
不忘初心 砥砺前行, Tomorrow Is Another Day ! 相关文章 一. 线程管理之Thread基础 二. 线程管理之线程池 三. 线程管理之ThreadLocal 四. 线程管理之A ...
- Android线程管理(一)
线程通信.ActivityThread及Thread类是理解Android线程管理的关键. 线程,作为CPU调度资源的基本单位,在Android等针对嵌入式设备的操作系统中,有着非常重要和基础的作用. ...
- 详解线程本地变量ThreadLocal
并发应用的一个关键地方就是共享数据.如果你创建一个类对象,实现Runnable接口,然后多个Thread对象使用同样的Runnable对象,全部的线程都共享同样的属性.这意味着,如果你在一个线程里改变 ...
- rtems线程管理与调度(一)
rtemsahi一个以线程为基本调度单位的实施操作系统,调度算法是基于优先级的抢占式线程调度,支持256个线程优先级,0代表最高优先级,主要用于内部线程,255是最低线程,是空闲线程的优先级,用户线程 ...
- [转]C++ 11 多线程--线程管理
转载地址:https://www.cnblogs.com/wangguchangqing/p/6134635.html 说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并 ...
- 并发基础(十) 线程局部副本ThreadLocal之正解
什么是ThreadLocal ThreadLocal是线程局部变量,所谓的线程局部变量,就是仅仅只能被本线程访问,不能在线程之间进行共享访问的变量.在各个Java web的各种框架中ThreadLoc ...
- C++ 11 多线程--线程管理
说到多线程编程,那么就不得不提并行和并发,多线程是实现并发(并行)的一种手段.并行是指两个或多个独立的操作同时进行.注意这里是同时进行,区别于并发,在一个时间段内执行多个操作.在单核时代,多个线程是并 ...
- C++多线程并发中线程管理
一.何为并发 刚开始接触计算机编程语言时,我们编写一个程序,在main入口函数中调用其它的函数,计算机按我们设定的调用逻辑来执行指令获得结果.如果我们想在程序中完成多个任务,可以将每个任务实现为一个函 ...
- 线程管理(九)使用本地线程变量
声明:本文是< Java 7 Concurrency Cookbook >的第一章, 作者: Javier Fernández González 译者:郑玉婷 校对:方腾飞 使用本地线程变 ...
最新文章
- Python踩坑指南(第二季)
- akae-arm9异常
- C++ Primer 5th笔记(chap 15 OOP)继承中的类作用域
- Robot Framework操作MySQL数据库和Oracle数据库
- 4位加法器的设计代码verilog_HDLBits:在线学习Verilog(六 · Problem 25-29)
- vim反向删除_VIM之操纵缓冲区列表
- 高效 Java Web 开发框架 JessMA v3.2.1 正式发布
- 讯飞C/C++语音合成基础篇
- SpringBoot总结之浅析自动化配置原理
- flexsim怎么设置传送带方向_Flexsim仿真教程学习(六)-分拣系统
- 飞天侠淘宝客源码8.0终结版仿卷皮模板-免费开源
- Spring学习04:事务控制(TransactionManager)
- 程序员容易发福的原因及解决办法
- 智能车基于RT1064+无线串口透传模块利用MATLAB辅助调节PID参数
- 【贪心法】黑白连线问题
- 思维导图带你了解22个职场学习网站!亲测好用
- w ndows10图标,win10桌面图标变白怎么解决
- 管道pipe-有名管道
- 在 Android 设备上搭建 Web 服务器
- 江苏省发布大数据引领推动融合发展专项行动计划
热门文章
- 使用 GDB 恢复堆栈信息
- C/C++:Windows编程—创建进程、终止进程、枚举进程、枚举线程、枚举DLL
- XAF-BI.Dashboard模块概述 web/win
- H3C DHCP服务器显示及维护
- Python-----规范化开发
- [leetcode]687. Longest Univalue Path
- 【CUDA开发】 Check failed: error == cudaSuccess (8 vs. 0) invalid device function
- html、css、js实现简易计算器
- mysql导出csv格式去除字段中的\n\r
- YY:马化腾的大漏招