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基础解析 ...
随机推荐
- css基础:样式属性
1.背景与前景:background-color::背景色,样式表优先级高. background-image:url(路径):设置背景图片 background-attachment:fixed:背 ...
- Phpstorm中使用SFTP
Phpstorm中经常会出现FTP连接失败的问题,这个时候我们可以使用SFTP来连接服务器. 1.添加服务器.tools--deployment--configuration/browse Remot ...
- IntelliJ IDEA使用(一)基本设置与类、方法模板设置
其实之前一直开发都是在使用的是Eclipse,但是最近在做Maven项目的时候要用IntelliJ IDEA,据说这个idea功能非常的强大,最近在使用的时候发现如果适应的真的是非常的强大.感觉是比E ...
- 基础知识(C#语法、数据库SQL Server)回顾与总结
前言 已经有大概一个多月没有更新博客,可能是开始变得有点懒散了吧,有时候想写,但是又需要额外投入更多的时间去学习,感觉精力完全不够用啊,所以为了弥补这一个多月的潜水,决定写一篇,衔接9月未写博客的空缺 ...
- Rsync for windows
说到电脑,我真是一屋子都是. 从房间到大厅,就已经有5台.这还没包括服务器. 虽然这5台电脑我最常用的也只是2~3台.其他的不是给朋友们来坐的时候打打游戏.就是给妈妈上网看看报纸. 不过我相信很多朋友 ...
- 201521123083《Java程序设计》第13周学习总结
本次作业参考文件 正则表达式参考资料 1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.bai ...
- java--利用exe4j生成.exe的可执行文件
工具:eclipse,exe4j,jre,这三个都可以直接在官方网站下载,下面所用到的都是最新版的. 前期准备:用eclipse编好需要生成.exe文件的project,另外exe4j需要一个注册码, ...
- 201521123095 《Java程序设计》第7周学习总结
1. 本章学习总结 **2. 书面作业* 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 该源代码验证该ArrayList中是否包含某个对象,contains的 ...
- 201521123076 《Java程序设计》第6周学习总结
1.本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖面 ...
- 201521123048 《Java程序设计》第5周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 2. 书面作业 1.代码阅读:Child压缩包内源代码 //child public class test extends P ...