014 ThreadLocal详解
一:ThreadLocal的原理
1.说明
ThreadLocal从字面意思来理解,是一个线程本地变量,也可以叫线程本地变量存储。有时候一个对象的变量会被多个线程所访问,这个时候就会有线程安全问题,当然可以使用synchronized关键字来为该变量加锁,进行同步处理来限制只能有一个线程来使用该变量,但是这样会影响程序执行的效率,这时ThreadLocal就派上了用场;
使用ThreadLocal维护变量的时候,会为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个当前变量。这样同时有多个线程访问该变量并不会相互影响,因为他们都是使用各自线程存储的变量,所以不会存在线程安全的问题。
同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式,前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问且互不影响。
2.源码

ThreadLocal实现原理:每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object。
也就是说ThreadLocal本身不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
值得注意的是图中(图片摘自网络)的虚线,表示ThreadLocalMap是使用ThreadLocal的弱引用作为key的,弱引用的对象在GC时会被回收。
3.ThreadLocal中有四个主要的方法
public T get() {} //获取当前线程中ThreadLocal副本
public void set(T value) {} //用来设置当前线程中ThreadLocal副本
public void remove() {} //移除当前线程中ThreadLocal副本
protected T initialValue() {} //是一个protected方法,一般使用时需要重写,默认返回为null
4.get方法的处理逻辑
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程中的ThreadLocalMap,每个线程都会有一个类型为ThreadLocalMap的inheritableThreadLocals变量
ThreadLocalMap map = getMap(t);
//如果当前线程中ThreadLocalMap为null,则返回初始化值
if (map != null) {
//获取map中ThreadLocal副本
ThreadLocalMap.Entry e = map.getEntry(this);
//获取存储的变量值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//返回初始化值,一般setInitialValue()需要重写,自定义初始化值
return setInitialValue();
}
Entry:
Entry类是内部类ThreadLocalMap中的内部类,集成了WeakReference类,如下所示:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
//与此ThreadLocal关联的值
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
此hash map的entries使用它的“主引用”字段作为键(它始终是ThreadLocal对象)继承自WeakReference类。 请注意,null键(即entry.get()== null)表示不再引用该键,因此可以从table中删除该entry。 这些entries在下面的代码中称为“陈旧entry”。
既然Entry继承了WeakReference,说明ThreadLocal使用了弱引用,如果entry.get()==null,当前ThreadLocal副本会立即被GC掉,避免高并发情况下出现内存溢出。
5.set逻辑
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//从当前线程中的ThreadLocalMap中获取存储的当前副本的值
ThreadLocalMap map = getMap(t);
//如果map不为空,覆盖当前副本中的值,否则新建副本
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
将此线程局部变量的当前线程副本设置为指定值。 大多数子类都不需要重写此方法,仅依靠initialValue()方法来设置线程局部变量值。
6.remove逻辑
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
删除当前线程ThreadLocal的变量值。 如果当前线程随后通过该线程从get()方法中获取ThreadLocal的变量值,那么它的值将通过调用initialValue()方法重新获取初始值,除非它的值是当前线程临时调用set()方法重新设置了值。 这可能导致在当前线程中多次调用initialValue()方法。
7.initialValue方法
protected T initialValue() {
return null;
}
这个方法默认返回是null,注意看一下它的修饰符是protected,一般情况这个方法是要使用者去实现的,设置默认的初始值。
二:
1.使用场景
ThreadLocal常用于解决数据库连接、session管理等
014 ThreadLocal详解的更多相关文章
- android Handler机制之ThreadLocal详解
概述 我们在谈Handler机制的时候,其实也就是谈Handler.Message.Looper.MessageQueue之间的关系,对于其工作原理我们不做详解(Handler机制详解). Messa ...
- 深入解析ThreadLocal 详解、实现原理、使用场景方法以及内存泄漏防范 多线程中篇(十七)
简介 从名称看,ThreadLocal 也就是thread和local的组合,也就是一个thread有一个local的变量副本 ThreadLocal提供了线程的本地副本,也就是说每个线程将会拥有一个 ...
- ThreadLocal详解(实现多线程同步访问变量)
ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量. 这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线 ...
- java核心知识点学习----多线程间的数据共享和对象独立,ThreadLocal详解
线程内的数据共享与对象独立,举例:张三给李四转钱,开启A线程去执行转钱这个动作,刚好同时王五给赵六转钱,开启B线程去执行转钱,因为是调用的同样一个动作或者说对象,所以如果不能保证线程间的对象独立,那么 ...
- ThreadLocal详解
ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量. 这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线 ...
- 【Java并发系列03】ThreadLocal详解
img { border: solid 1px } 一.前言 ThreadLocal这个对象就是为多线程而生的,没有了多线程ThreadLocal就没有存在的必要了.可以将任何你想在每个线程独享的对象 ...
- 并发系列(2)之 ThreadLocal 详解
本文将主要结合源码讲述 ThreadLocal 的使用场景和内部结构,以及 ThreadLocalMap 的内部结构:另外在阅读文本之前只好先了解一下引用和 HashMap 的相关知识,可以参考 Re ...
- ThreadLocal详解,ThreadLocal源码分析,ThreadLocal图解
本文脉路: 概念阐释 ----> 原理图解 ------> 源码分析 ------> 思路整理 ----> 其他补充. 一.概念阐述. ThreadLocal 是一个为 ...
- 【Java深入研究】7、ThreadLocal详解
ThreadLocal翻译成中文比较准确的叫法应该是:线程局部变量. 这个玩意有什么用处,或者说为什么要有这么一个东东?先解释一下,在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线 ...
随机推荐
- 10分钟学会js处理json常用方法
一.json定义 JSON ( JavaScript Object Notation) ,它是一串字符串 只不过元素会使用特定的符号标注. {} 双括号表示对象 [] 中括号表示数组 "&q ...
- ffmpeg 把视频转换为图片
ffmpeg -i "Tail of Hope.mp4" -r 1 -q:v 2 -f image2 pic-%03d.jpeg
- maven mvn跳过生成javadoc 打包报错
遇到javadoc用maven打包报错的问题,起初没发现javadoc,后发现并在pom看到了javadoc的配置. [ERROR] Failed to execute goal org.apache ...
- 使用CEfSharp之旅(8)CEFSharp 使用代理 更换位置IP
直接上代码: var settings = new CefSettings(); settings.CachePath = "cache"; settings.CefCommand ...
- php导出表格两种方法 ——PhpExcel的列子
php常用的导出表格有两种方法,第一种是输出表格,这种方法打开的时候有警告提示,一般导出表格会用phpexcel,这个导出比较灵活,而且还可以设置表格的样式. 第一种导出例子 /** * 执行导出 * ...
- 解决Mac外接显示器字体模糊的问题
Mac外接显示器时,除非接的是Apple自家的显示器“ACD”,不然一般会遇到字体模糊发虚的问题.在终端中执行命令: defaults -currentHost write -globalDomain ...
- MySql数据封装操作类
1.先引用MySQL的DLL文件 using System; using System.Collections.Generic; using System.Linq; using System.Tex ...
- [ARIA] aria-describedby & aria-labelledby
When to use describedby: For example you have a close button: <button aria-describedby="clos ...
- Parametric and Nonparametric Algorithms
即参数化算法和非参数化算法. 参数化机器学习算法 可以大大简化学习过程,也可以限制可以学到的东西,将函数简化为已知形式的算法称为参数化机器学习算法.算法包括两个步骤: 为函数选择一个form. 从训练 ...
- CollectionUtils.select用法
import java.util.ArrayList;import java.util.List; import org.apache.commons.collections.CollectionUt ...