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简述和分类 我们知道在一个进程中,所有线程是共享同一个地址空间的.所以,如果一个变量是全局的或者是静态的,那么所有线程访问的是同一份,如果某一个线 ...
随机推荐
- orm功能封装
封装功能: 查 : select **kwargs.keys() --返回-> obj -转为-->list [{},{}] ---> [obj,obj] class Models( ...
- 理解Docker :Docker 网络
本系列文章将介绍 Docker的相关知识: (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 ...
- Sonar错误 Invoke method(s) only conditionally
sonarLint总是报错: Invoke method(s) only conditionally 代码如下: if(us != null){ logger.info("Log this: ...
- Canal - 数据同步 - 阿里巴巴 MySQL binlog 增量订阅&消费组件
背景 早期,阿里巴巴 B2B 公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求 ,主要是基于trigger的方式获取增量变更.从 2010 年开始,公司开始逐步尝试数据库日志解析,获取增量变 ...
- java http get/post请求
一.http get/post请求 /** * @Description httpPost请求 */ public static String httpPost(String url, String ...
- window操作系统分区
注意GPT分区模式不能创建扩展分区和逻辑分区
- ios兼容性收集整理
1. ios系统兼input输入框光标问题 异常现象:苹果手机文本输入框样式异常——光标聚焦到文本框,光标高度充满文本框,输入内容,光标高度为文本框上边框到输入内容底部: 光标聚焦: 输入内容: 异常 ...
- Python3基础 import...as 给导入的模块起别名
Python : 3.7.3 OS : Ubuntu 18.04.2 LTS IDE : pycharm-community-2019.1.3 ...
- 【C#】使用C# 读取Http的Post数据
private string Post(string num) { Encoding myEncoding = Encoding.GetEncoding("gb2312"); // ...
- Flutter中极光推送的使用----jpush_flutter
原文地址:https://www.cnblogs.com/niceyoo/p/11095994.html 1.申请极光账号和建立应用 极光推送的官方网址为:https://www.jiguang.cn ...