Java线程本地存储ThreadLocal
前言
- ThreadLocal 是一种 无同步 的线程安全实现
- 体现了
Thread-Specific Storage模式:即使只有一个入口,内部也会为每个线程分配特有的存储空间,线程间 没有共享资源 - 本文将总结
ThreadLocal的用法与实现细节,希望能帮上忙

ThreadLocal 思维导图

线程安全 示意图
1. 用法
ThreadLocal 的用法很简单, ThreadLocal 提供了下列的public与protected方法,本文将引用 android.os.Looper.java 为例子讲解 ThreadLocal 的用法:

ThradlLocal UML类图
// /frameworks/base/core/java/android/os/Looper.java
public class Looper {
// ...
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 设置线程局部变量的值
sThreadLocal.set(new Looper(quitAllowed));
}
public static Looper myLooper() {
// 获取线程局部变量的值
return sThreadLocal.get();
}
public static void prepare() {
prepare(true);
}
// ...
}
ThreadLocal为 static final变量 ,泛型参数为Looper,表示接受Looper类型的值Looper#prepare()中调用ThreadLocal#set()设置 变量的值,不同线程设置的值互不干扰,不会相互覆盖Looper#myLooper()中调用ThreadLocal#get()获取 变量的值,不同线程获取的值互不干扰

Timethreads图 - 01
子类 重写
ThreadLocal#initialValue(),可以设置变量的初始值,我们查看相关源码:public ConcurrentHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;
// Find power-of-two sizes best matching arguments
int sshift = 0;
int ssize = 1;
while (ssize < concurrencyLevel) {
++sshift;
ssize <<= 1;
}
this.segmentShift = 32 - sshift;//用于高位,判断落在哪个Segment
this.segmentMask = ssize - 1;//用于取模。之前在hashmap的indexFor方法有提过。2的n次方-1
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
cap <<= 1;
// create segments and segments[0]
Segment<K,V> s0 =
new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
(HashEntry<K,V>[])new HashEntry[cap]);//初始化第一个位置的Segment
Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];//初始化Segments
UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
this.segments = ss;
}- 在
ThreadLocal#get()中尝试获取变量的值,如果为空则会调用ThreadLocal#setInitialValue()设置设置初始值 - 懒初始化 :直到第一次调用
get()才会设置初值 - 默认 :设置初始值为 null
- 在
ThreadLocal#remove()用于 移除 变量之前存储的值。如果在当前线程下次调用ThreadLocal#get()时,还没有set()新的值,依旧会使用ThreadLocal#setInitialValue()设置初始值。
到这里我们就可以总结 ThreadLocal 的生命周期,如下图所示:

ThreadLocal生命周期 示意图
2. 编程规约
记得吗?《阿里巴巴Java开发手册》中提到过关于 ThreadLocal 的编程规约,如下所示:
5.【强制】
SimpleDateFormate是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁,或者使用DateUtils工具类正例:
private static final ThreadLocal<DataFormat> df = new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue(){
return new SimpleDateFormat("yyyy-MM-dd");
}
};说明:如果是JDK8的应用,可以使用
Instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe.15.【参考】(原文过于啰嗦,以下为笔者转述)
ThreadLocal变量建议使用 static 修饰,可以保证变量在类初始化时创建,所有类实例可以共享同一个静态变量。注意到了吗?在文章开头的Looper.java源码中,
ThreadLocal变量就是使用static修饰的
看到这里,相信你已经掌握了 ThreadLocal 的用法,下一篇文章将深入 ThreadLocal 的内部,探讨数据结构的实现细节。
Segment
ConcurrentHashMap是由多个Segment组成的,Segment继承了ReentrantLock,每次加锁都是对某个Segment,不会影响其他Segment,达到了锁分离(也叫分段锁)的作用。
每个Segment又包含了HashEntry数组,HashEntry是一个链表。如下图所示:

concurrencyLevel:并发数,默认16,直接影响segmentShift和segmentMask的值,以及Segment的初始化数量。Segment初始化的数量,为最接近且大于的办等于2的N次方的值,比如concurrencyLevel=16,Segment数量为16,concurrencyLevel=17,Segment数量为32。segmentShift的值是这样的,比如Segment是32,相对于2的5次方,那么他的值就是32-5,为27,后面无符号右移27位,也就是取高5位的时候,就是0到31的值,此时Segment的下标也是0到31,取模后对应着每个Segment。segmentMask就是2的n次方-1,这边n是5,用于取模。之前在hashmap的indexFor方法有提过。
Java线程本地存储ThreadLocal的更多相关文章
- Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage 规范标准化草案解决方案ThreadStatic
Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage 规范标准化草案解决方案ThreadStatic 1.1. ThreadLocal 设计模式1 1.2. ...
- 线程本地存储 ThreadLocal
线程本地存储 · 语雀 (yuque.com) 线程本地存储提供了线程内存储变量的能力,这些变量是线程私有的. 线程本地存储一般用在跨类.跨方法的传递一些值. 线程本地存储也是解决特定场景下线程安全问 ...
- JAVA线程本地变量ThreadLocal和私有变量的区别
ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些. 所以,在Java中编写线程局部变量的代码相对来说要笨 ...
- ThreadLocal(线程本地存储)
1. ThreadLocal,即线程本地变量或线程本地存储. threadlocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的 ...
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
原文链接地址:http://www.cppblog.com/Tim/archive/2012/07/04/181018.html 本文为线程本地存储TLS系列之分类和原理. 一.TLS简述和分类 我们 ...
- 线程本地存储(Thread Local Storage, TLS)简单分析与使用
在多线程编程中, 同一个变量, 如果要让多个线程共享访问, 那么这个变量可以使用关键字volatile进行声明; 那么如果一个变量不想使多个线程共享访问, 那么该怎么办呢? 呵呵, 这个办法就是TLS ...
- .NET:线程本地存储、调用上下文、逻辑调用上下文
.NET:线程本地存储.调用上下文.逻辑调用上下文 目录 背景线程本地存储调用上下文逻辑调用上下文备注 背景返回目录 在多线程环境,如果需要将实例的生命周期控制在某个操作的执行期间,该如何设计?经典的 ...
- C# 线程本地存储 调用上下文 逻辑调用上下文
线程本地存储 using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleAppTest ...
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
本文为线程本地存储TLS系列之分类和原理. 一.TLS简述和分类 我们知道在一个进程中,所有线程是共享同一个地址空间的.所以,如果一个变量是全局的或者是静态的,那么所有线程访问的是同一份,如果某一个线 ...
随机推荐
- win10系统2分钟睡眠
https://blog.csdn.net/widenstage/article/details/78982722 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSe ...
- Pytest权威教程19-编写钩子(Hooks)方法函数
目录 编写钩子(Hooks)函数 钩子函数验证和执行 firstresult: 遇到第一个有效(非None)结果返回 hookwrapper:在其他钩子函数周围执行 钩子(Hooks)函数排序/调用示 ...
- linux高性能服务器编程 (二) --IP协议详解
第二章 IP协议详解 什么是IP协议:IP 协议是TCP/IP协议族的动力,它为上层提供了无状态.无连接.不可靠的服务. IP 头部信息:头部信息会出现在每一个IP数据报上,便于记录IP通信的源端IP ...
- 访问者模式(Visitor Patten)
参考文章: http://www.importnew.com/15561.html 定义: 封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作. um ...
- 【译文】走出Java ClassLoader迷宫 Find a way out of the ClassLoader maze
本文是一篇译文.原文:Find a way out of the ClassLoader maze 对于类加载器,普通Java应用开发人员不需要了解太多.但对于系统开发人员,正确理解Java的类加载器 ...
- gradle/gradle plugin/Android studio关系
gradle - 构建工具,存储于Users/stono/.gradle/wrapper/dists Adroid Studio- IDE Gradle plugin - 在AS中使用Gradle的插 ...
- java8学习
1.Function函数 public static void main(String[] args) { TestController t = new TestController(); new T ...
- 使用python脚本批量设置nginx站点的rewrite规则
一般情况下,配置rewrite重写规则使用shell脚本即可: 把url拼凑成1,2文件中中的格式,运行 chongxie.sh 即可生成我们需要的rewrite规则 [root@web01:/opt ...
- (转)react 项目构建
原文:https://segmentfault.com/a/1190000016342792 写在前面 每次构建react项目的时候都会配置一大堆东西,时间久了就会忘记怎么配置.为了方便自己记忆也为了 ...
- some try on func swap about & and *
import "fmt" func swap(x,y *int ) { //x ,y = y,x //fault /* t := *x *x = *y *y = t */ //tr ...