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基础解析 ...
随机推荐
- php sql uuid 32位
最近表中id需要用到此值来作为唯一主键 其含义是通用唯一识别码.具体好处及应用可百度百科,链接给你https://baike.baidu.com/item/UUID/5921266?fr=aladdi ...
- PHP发送邮件功能--ThinkPHP3.2.3
首先第一步 :在网上down了一个PHPMailer插件,插件地址>https://github.com/PHPMailer/PHPMailer下载解压后,这里我们只需要用到其中两个文件,如 ...
- Freemarker的使用方法
1. Freemarker概念 1.1 什么是freemarker FreeMarker是一个用Java语言编写的模板引擎,它基于模板来生成文本输出.FreeMarker与Web容器无关,即在Web运 ...
- 解决在linux环境下面不显示验证码的问题
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt235 解决在linux环境下面不显示验证码的问题1.tomcat ...
- Linux-grep 命令和find 命令 (6)
grep与find区别: grep:查找指定目录下过滤文本文件中行数据 find:查找指定目录下文件 grep使用 格式: grep "text" * [选项] 选项: ...
- CSS文本效果
前面的话 本文将详细介绍CSS文本效果 凸版印刷效果 这种效果尤其适用于中等亮度背景配上深色文字的场景:但它也可用于深色底.浅色字的场景,只要文字不是黑色并且背景不是纯黑或纯白就行 [浅色背景深色文本 ...
- Sublime Text编辑器如何隐藏顶部的菜单栏
隐藏前: 解决办法: 1.按住Ctrl+Shifp+p,出现一个框,在框里输入“view:”,出现了如下界面 2.选择:“View:Toggle Menu”即可.结果为: 大功告成!!!!
- 201521123024 《java程序设计》 第12周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容.
- foreach_and_函数
foreach(对集合每个元素的引用 in 集合){}例: int[] a = new int[5]{1,2,3,4,5,}; foreach(int b in a) { //b就是a中的每个元素 } ...
- sublime text3 好用的插件!!!
1.首先,你要保证sublime有Package Control,所以,如果没有,那么将Ctrl+`打开sublime控制台,将下列代码复制进去! import urllib.request,os; ...