Loading [MathJax]/jax/input/TeX/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java 并发编程·ThreadLocal

Java 并发编程·ThreadLocal

作者头像
数媒派
发布于 2022-12-01 03:43:13
发布于 2022-12-01 03:43:13
28300
代码可运行
举报
文章被收录于专栏:产品优化产品优化
运行总次数:0
代码可运行

ThreadLocal

对于多任务,Java 标准库提供的线程池可以方便地执行这些任务,同时复用线程。那么如何在一个线程内传递状态?

如下栗子,一个内部需要调用若干其他方法,同时传递参数 user。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void process(User user) {
    checkPermission(user);
    doWork(user);
    saveStatus(user);
    sendResponse(user);
}

这种在一个线程中,横跨若干方法调用,需要传递的对象,我们通常称之为上下文(Context),它是一种状态,可以是用户身份、任务信息等。

给每个方法增加一个 context 参数非常麻烦,而且有些时候,如果调用链有无法修改源码的第三方库,User 对象就传不进去了。

Java 标准库提供了一个特殊的 ThreadLocal,它可以在一个线程中传递同一个对象。

ThreadLocal 实例通常总是以静态字段初始化如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static ThreadLocal<String> threadLocalUser = new ThreadLocal<>();

使用方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void processUser(user) {
    try {
        threadLocalUser.set(user);
        step1();
        step2();
    } finally {
        threadLocalUser.remove();
    }
}

void step1() {
    User u = threadLocalUser.get();
    printUser();
}

void step2() {
    User u = threadLocalUser.get();
    checkUser(u.id);
}

注意到普通的方法调用一定是同一个线程执行的,所以,step1()step2() 方法内,threadLocalUser.get() 获取的 User 对象是同一个实例。

实际上,可以把 ThreadLocal 看成一个全局 Map<Thread, Object>,每个线程获取 ThreadLocal 变量时,总是使用 Thread 自身作为 key:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Object threadLocalValue = threadLocalMap.get(Thread.currentThread());

因此,ThreadLocal 相当于给每个线程都开辟了一个独立的存储空间,各个线程的 ThreadLocal 关联的实例互不干扰。

最后,特别注意 ThreadLocal 一定要在 finally 中清除。这是因为当前线程执行完相关代码后,很可能会被重新放入线程池中,如果 ThreadLocal 没有被清除,该线程执行其他代码时,会把上一次的状态带进去。

为了保证能释放 ThreadLocal 关联的实例,我们可以通过 AutoCloseable 接口配合 try (resource) {...} 结构,让编译器自动为我们关闭。例如,一个保存了当前用户名的 ThreadLocal 可以封装为一个 UserContext 对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class UserContext implements AutoCloseable {

    static final ThreadLocal<String> ctx = new ThreadLocal<>();

    public UserContext(String user) {
        ctx.set(user);
    }

    public static String currentUser() {
        return ctx.get();
    }

    @Override
    public void close() {
        ctx.remove();
    }
}

使用方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try (var ctx = new UserContext("Bob")) {
    // 可任意调用UserContext.currentUser():
    String currentUser = UserContext.currentUser();
} // 在此自动调用UserContext.close()方法释放ThreadLocal关联对象

这样就在 UserContext 中完全封装了 ThreadLocal,外部代码在 try (resource) {...} 内部可以随时调用 UserContext.currentUser() 获取当前线程绑定的用户名。

小结

ThreadLocal 空间换时间,synchronized 时间换空间。

  • ThreadLocal 表示线程的“局部变量”,它确保每个线程的 ThreadLocal 变量都是各自独立的;
  • ThreadLocal 适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递);
  • 使用 ThreadLocal 要用 try ... finally 结构,并在 finally 中清除。

DEMO

ThreadLocal SimpleDateFormat

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 10 个线程执行 1000 次打印格式化日期,每个线程有自己的格式化对象
 */
public class ThreadLocalNormalUsage03 {

    private static ExecutorService threadPool = Executors.newFixedThreadPool(10);

    private static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i++) {
            int finalI = i;
            threadPool.submit(() -> {
                String date = new ThreadLocalNormalUsage03().date(finalI);
                System.out.println(date);
            });
        }
        threadPool.shutdown();
    }

    private String date(int seconds) {
        Date date = new Date(1000 * seconds);
        SimpleDateFormat simpleDateFormat = dateFormatThreadLocal.get();
        return simpleDateFormat.format(date);
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java并发——ThreadLocal(十二)
ThreadLocal 用于解决多线程环境下的线程安全问题。ThreadLocal为每个线程访问的变量提供了一个独立的副本,线程在访问这个变量时,访问的都是自己的副本数据,从而线程安全,即ThreadLocal为变量提供了线程隔离。
翰墨飘香
2024/08/14
1560
ThreadLocal相关知识点
每个线程需要一个独享的对象(通常是工具类,典型需要使用的类有SimpleDateFormat和Random),这个对象的特点通常是工具类,这个工具类由于它本身不是线程安全的,所以有多个线程共享同一个静态工具类的话,会有很大风险的,所以需要用到ThreadLocal来帮我们每个线程都创建一个独享的对象,而线程和线程之间呢拥有的工具类是不同的示例,所以之间并不会影响。
害恶细君
2022/11/22
2120
ThreadLocal相关知识点
ThreadLocal详解
典型场景1:每个线程需要一个独享的对象(通常是工具类,典工具类型需要使用的类有SimpleDateFormat和Random)
砖业洋__
2023/05/06
3210
ThreadLocal详解
Java 中的 ThreadLocal 简介-Java快速进阶教程
在本教程中,我们将研究java.lang包中的ThreadLocal构造。这使我们能够单独存储当前线程的数据,并简单地将其包装在特殊类型的对象中。
jack.yang
2025/04/05
1340
ThreadLocal不好用?那是你没用对!
在 Java 中,如果要问哪个类使用简单,但用好最不简单?我想你的脑海中一定会浮现出一次词——“ThreadLocal”。 ​
磊哥
2021/05/17
5580
ThreadLocal不好用?那是你没用对!
聊聊并发编程的10个坑
说实话,在java中并发编程是一大难点,至少我是这么认为的。不光理解起来比较费劲,使用起来更容易踩坑。
苏三说技术
2022/08/25
4810
聊聊并发编程的10个坑
服了,一个ThreadLocal被问出了花
地铁上,小帅无力地倚靠着杆子,脑子里尽是刚才面试官的夺命连环问,“用过ThreadLocal么?ThreadLocal是如何解决共享变量访问的安全性的呢?你觉得啥场景下会用到ThreadLocal? 我们在日常用ThreadLocal的时候需要注意什么?ThreadLocal在高并发场景下会造成内存泄漏吗?为什么?如何避免?......”
程序员老猫
2024/02/22
1840
服了,一个ThreadLocal被问出了花
深入理解ThreadLocal:拨开迷雾,探究本质
> ThreadLocal是JDK1.2提供的一个工具,它为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,解决共享参数的频繁传递与线程安全等问题。如果开发者掌握了ThreadLocal用法与原理,那么使用起来将得心应手,那么请跟随本文的节奏,拨开迷雾,探究本质吧!
itlemon
2020/03/31
2510
探索JAVA并发 - ThreadLocal
SimpleDateFormat是我们常用的日期格式化工具,但熟悉的朋友都知道它是线程不安全的。
acupt
2019/08/26
4250
探索ThreadLocal的使用与SimpleDateFormat的多线程问题
在Java的多线程编程中,我们常常会遇到某些类在多线程环境下不安全的问题,例如SimpleDateFormat。由于SimpleDateFormat不是线程安全的,直接在多线程中共享一个实例会导致各种奇怪的问题。因此,我们需要寻找一种有效的方法来使每个线程拥有一个独立的SimpleDateFormat实例。本文将深入探讨如何利用ThreadLocal实现这个目标,并分析其中的一些陷阱和解决方案。
九转成圣
2024/06/05
2220
抛出这8个问题,检验一下你到底会不会ThreadLocal,来摸个底~
ThreadLocal类是用来提供线程内部的局部变量。让这些变量在多线程环境下访问(get/set)时能保证各个线程里的变量相对独立于其他线程内的变量。
Java编程指南
2020/07/24
7460
抛出这8个问题,检验一下你到底会不会ThreadLocal,来摸个底~
浅谈ThreadLocal
ThreadLocal因为内存泄漏问题早已在江湖中声名远扬,引得一众开发人员的吐槽。于是,ThreadLocal 的设计者之一Josh Bloch不得不出来辟谣:ThreadLocal的设计毫无问题,而且历经数次优化后其性能越来越好,内存泄漏是由开发者误用造成的,我们不背这个锅!由此可见,ThreadLocal 是有一定上手门槛的,希望大家在读完本文后可以正确地使用它。
程序猿杜小头
2022/12/01
4770
浅谈ThreadLocal
ThreadLocal之美!
常用于例如SimpleDateFormatter这样非线程安全的工具类上,比如需要1000次用到这个工具类,想要不频繁的创建导致的开销,以及高效的避免线程安全问题,就可以用
Joseph_青椒
2023/08/06
2750
聊聊并发编程的12种业务场景
之前我发表的一篇《聊聊并发编程的10个坑》,在全网广受好评。说明了这类文章还是比较有价值的,接下来,打算继续聊聊并发编程这个话题。
苏三说技术
2022/08/25
4320
聊聊并发编程的12种业务场景
Java并发学习之ThreadLocal使用及原理介绍
ThreadLocal使用及原理介绍 线程本地变量,每个线程保存变量的副本,对副本的改动,对其他的线程而言是透明的(即隔离的) 1. 使用姿势一览 使用方式也比较简单,常用的三个方法 // 设置当前线程的线程局部变量的值 void set(Object value); // 该方法返回当前线程所对应的线程局部变量 public Object get(); // 将当前线程局部变量的值删除 public void remove(); 下面给个实例,来瞅一下,这个东西一般的使用姿势。通常要获取线程变量,
一灰灰blog
2018/02/06
5140
Java并发-ThreadLocal
每个线程内部都有一个ThreadLocalMap,每个ThreadLocalMap里面都有一个Entry[]数组,Entry对象由ThreadLocal和数据组成。
lpe234
2021/03/02
4620
再谈ThreadLocal
大家对于ThreadLocal肯定很熟悉了,但是真正在项目中使用过的估计就不多了,有的牛人也许已经使用n多次了。
田维常
2019/07/16
8130
再谈ThreadLocal
深入理解ThreadLocal
在多线程编程中,线程间的数据共享是一个重要的课题。虽然共享数据有很多方法,但有时我们希望每个线程都有自己的独立数据副本,以避免竞争条件和并发问题。在这种情况下,Java中的ThreadLocal类提供了一种优雅的解决方案。本文将深入探讨ThreadLocal的概念、使用方法、实现原理以及实际应用。
九转成圣
2024/05/24
1070
Java并发:ThreadLocal的简单介绍
前面在线程的安全性中介绍过全局变量(成员变量)和局部变量(方法或代码块内的变量),前者在多线程中是不安全的,需要加锁等机制来确保安全,后者是线程安全的,但是多个方法之间无法共享
汤圆学Java
2021/05/27
3110
并发设计模式实战系列(7):Thread Local Storage (TLS)
今天为大家带来的是并发设计模式实战系列,第七章Thread Local Storage (TLS),废话不多说直接开始~
摘星.
2025/05/20
1230
相关推荐
Java并发——ThreadLocal(十二)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档