java基础解析系列(七)---ThreadLocal原理分析
java基础解析系列(七)---ThreadLocal原理分析
目录
- java基础解析系列(一)---String、StringBuffer、StringBuilder
- java基础解析系列(二)---Integer缓存及装箱拆箱
- java基础解析系列(三)---HashMap原理
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
- java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别
- java基础解析系列(六)---注解原理及使用
- 这是我的博客目录,欢迎阅读
作用
- 与同步机制区分开来,同步机制是为了解决在共享情况下并发导致的问题。而ThreadLocal是避免了共享
- 在多线程情况下,为了避免共享,我们可以采用多线程多实例的方式,也可以使用ThreadLocal来避免共享冲突
什么是ThreadLocal
- ThreadLocal提供了线程本地变量,它可以保证访问到的变量属于当前线程,每个线程都保存有一个变量副本,每个线程的变量都不同。ThreadLocal相当于提供了一种线程隔离,将变量与线程相绑定
实验
public class T {
ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
public void set() {
longLocal.set(Thread.currentThread().getId());
}
public long getLong() {
return longLocal.get();
}
public static void main(String[] args) throws InterruptedException {
final T test = new T();
test.set();
Thread thread1 = new Thread(){
public void run() {
test.set();
System.out.println("线程一:"+test.getLong());
};
};
thread1.start();
thread1.join();
System.out.println("main线程:"+test.getLong());
System.out.println("没有发生值的覆盖,两个线程保存的值是不同的");
}
}
- 输出: 线程一:11 main线程:1 没有发生值的覆盖,两个线程保存的值是不同的
- 证明ThreadLocal确实为变量在每个线程中都创建了一个副本
Thread的成员
ThreadLocal.ThreadLocalMap threadLocals = null;
- Thread有一个ThreadLocalMap成员
ThreadLocal的set方法
179 public void set(T value) {
180 Thread t = Thread.currentThread();
181 ThreadLocalMap map = getMap(t);
182 if (map != null)
183 map.set(this, value);
184 else
185 createMap(t, value);
186 }
- 181行通过当前线程获取ThreadLocalMap
212 ThreadLocalMap getMap(Thread t) {
213 return t.threadLocals;
214 }
- 185行如果当前线程的成员threadLocals还是空的,创建一个map
224 void createMap(Thread t, T firstValue) {
225 t.threadLocals = new ThreadLocalMap(this, firstValue);
226 }
- 将当前的ThreadLocal对象和value作为参数
328 ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
329 table = new Entry[INITIAL_CAPACITY];
330 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
331 table[i] = new Entry(firstKey, firstValue);
332 size = 1;
333 setThreshold(INITIAL_CAPACITY);
334 }

- 通过ThreadLocalMap的构造方法可以看到,该方法创建一个Entry数组,然后通过传入的key(当前ThreadLocal对象)计算在数组中的下标,然后将Entry放入数组。
- Thread的ThreadLocalMap存放的Entry,键是不同的ThreadLoacal对象,也就是说一个线程绑定多个ThreadLocal对象
- 那么也就是说,ThreadLocal设置值的时候,这个值是存放在当前线程的一个map(这个map存放了多个ThreadLocal对象)里面,因此,不同线程之间即避免了共享
ThreadLocalMap的set方法
416 private void set(ThreadLocal key, Object value) {
417
418 // We don't use a fast path as with get() because it is at
419 // least as common to use set() to create new entries as
420 // it is to replace existing ones, in which case, a fast
421 // path would fail more often than not.
422
423 Entry[] tab = table;
424 int len = tab.length;
425 int i = key.threadLocalHashCode & (len-1);
426
427 for (Entry e = tab[i];
428 e != null;
429 e = tab[i = nextIndex(i, len)]) {
430 ThreadLocal k = e.get();
431
432 if (k == key) {
433 e.value = value;
434 return;
435 }
436
437 if (k == null) {
438 replaceStaleEntry(key, value, i);
439 return;
440 }
441 }
442
443 tab[i] = new Entry(key, value);
444 int sz = ++size;
445 if (!cleanSomeSlots(i, sz) && sz >= threshold)
446 rehash();
447 }
- 分析这段代码,他解决hash冲突的办法不同与hashmap使用链表来解决冲突问题。通过计算key的hashcode获取数组中的下标后,然后进入427行,432判断要放入的键是否和该下标中原来的键相同,是的话进行值的覆盖。如果为空的,放入该Entry。如果不同的话且不为空,看当前下标+1的位置,同样进入循环。依次执行下去。
ThreadLocal的get方法
142 public T get() {
143 Thread t = Thread.currentThread();
144 ThreadLocalMap map = getMap(t);
145 if (map != null) {
146 ThreadLocalMap.Entry e = map.getEntry(this);
147 if (e != null)
148 return (T)e.value;
149 }
150 return setInitialValue();
151 }
- 可以发现,执行get方法的时候,也是先获取当前线程,然后获得该线程的一个ThreadLocalMap成员,通过这个map和和当前的ThreadLocal对象作为键,来获取value
内存泄露
271 static class Entry extends WeakReference<ThreadLocal> {
- 如果ThreadLocal不使用弱引用(这篇文章有介绍),那么当ThradLocal th=null时候,因为ThreadLocalMap仍然有th的强引用,所以并不能回收。而如果key使用弱引用的时候,th为null的时候,下次回收的时候就会将这个key回收
- 但是有一个问题,虽然这个key可以被回收,但是这个value仍然有强引用,并不能回收。如果当前线程不结束,并且不调用set/get/remove方法(这些方法会对key为null的entry进行释放),这片内存会被一直占用。这就是内存泄露的原因
- 因此在用完ThreadLocal的时候,记得执行remove方法,避免内存泄露
简单总结
- 同一个ThreadLocal对象,不同的线程,不同的ThreadLocal对象的值
我觉得分享是一种精神,分享是我的乐趣所在,不是说我觉得我讲得一定是对的,我讲得可能很多是不对的,但是我希望我讲的东西是我人生的体验和思考,是给很多人反思,也许给你一秒钟、半秒钟,哪怕说一句话有点道理,引发自己内心的感触,这就是我最大的价值。(这是我喜欢的一句话,也是我写博客的初衷)
作者:jiajun 出处: http://www.cnblogs.com/-new/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】,希望能够持续的为大家带来好的技术文章!想跟我一起进步么?那就【关注】我吧。
java基础解析系列(七)---ThreadLocal原理分析的更多相关文章
- java基础解析系列(六)---注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer缓存及 ...
- java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理
fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...
- java基础解析系列(九)---String不可变性分析
java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...
- java基础解析系列(十)---ArrayList和LinkedList源码及使用分析
java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...
- java基础解析系列(十一)---equals、==和hashcode方法
java基础解析系列(十一)---equals.==和hashcode方法 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系 ...
- java基础解析系列(六)---深入注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...
- java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现
java基础解析系列(四)---LinkedHashMap的原理及LRU算法的实现 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析 ...
- java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别
java基础解析系列(五)---HashMap并发下的问题以及HashTable和CurrentHashMap的区别 目录 java基础解析系列(一)---String.StringBuffer.St ...
- java基础解析系列(二)---Integer
java基础解析系列(二)---Integer 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容. 目录 java基础解析 ...
随机推荐
- 零基础到精通Web渗透测试的学习路线
小编相信很多新手都会遇到以下几个问题 1.零基础想学渗透怎么入手? 2.学习web渗透需要从哪里开始? 这让很多同学都处于迷茫状态而迟迟不下手,小编就在此贴给大家说一下web渗透的学习路线,希望对大家 ...
- 史上最全前端面试题(含答案)-B篇
面试有几点需要注意面试题目: 根据你的等级和职位变化,入门级到专家级:范围↑.深度↑.方向↑.题目类型: 技术视野.项目细节.理论知识型题,算法题,开放性题,案例题.进行追问: 可以确保问到你开始不懂 ...
- MySQL57安装图解
MySQL57安装图解... ============================= 0-需要准备的安装包 =================== 1在百度下载MySQl ============ ...
- Groovy读取文件信息
1. eachLine -- 打开和读取文件的每一行 new File("foo.txt").eachLine { println it.toUpperCase(); } 2. r ...
- github开源项目学习-front-end-collect
About 项目地址 项目预览demo(githubio加载较慢) 开源项目fork自:https://github.com/foru17/front-end-collect 此文章是对此开源项目使用 ...
- 马士兵讲jsp项目--BBS项目分析笔记
1 导言 作为一个新手JAVA程序员,我相信很多人和我一样急切的想要寻找项目来做,这里我把马士兵老师讲的JAVA WEB的第一个小项目拿过来给大家分享一下. 首先,对一个项目而言我们先要认识清楚我们要 ...
- Porita详解----Items
Items(项目) 一个item是指从目标网站上爬取的一条单独的数据.例如从京东网站上爬取的一款小米6手机的信息.大家应该对 item (项目)和 item definition(项目定义)做一个区分 ...
- flex弹性布局学习
一.介绍 flex( flexible box:弹性布局盒模型),是2009年w3c提出的一种可以简洁.快速弹性布局的属性.主要思想是给予容器控制内部元素高度和宽度的能力.目前已得到以下浏览器支持: ...
- Redis 常用数据结构及其控制命令整合
Redis 键值支持5种基本结构,分别是字符串,列表,哈希,集合,有序集合.每一种数据结构都有对应的取值和设值命令,辅助命令,除此之外,还有一些全局命令,用来管理Redis存储的所有 键. 全局命令 ...
- NHibernte教程(10)--关联查询
本节内容 关联查询引入 一对多关联查询 1.原生SQL关联查询 2.HQL关联查询 3.Criteria API关联查询 结语 关联查询引入 在NHibernate中提供了三种查询方式给我们选择:NH ...