写点什么

【连载 13】ThreadLocal 类

作者:FunTester
  • 2025-01-21
    河北
  • 本文字数:1888 字

    阅读完需:约 6 分钟

2.8 ThreadLocal

在使用 Java 进行性能测试的过程中,将线程共享的变量通过用例设计优化转换成线程独享,是一种非常高效的解决线程安全问题的思路。java.lang.ThreadLocal 可以不必提前确定线程的数量,不必提前分配每个线程所需要的对象,直接全局定义一个 java.lang.ThreadLocal 对象,在多线程编程中使用 java.lang.ThreadLocal 提供的操作 API 即可完成线程独享对象的创建、修改和其他管理操作。

2.8.1 基础方法

ThreadLocal 实现原理基于每个线程都维护一个 ThreadLocalMap,这个映射表的键是 ThreadLocal 实例,值是对应的线程局部变量。这样,每个线程都可以拥有自己的独立变量副本,不受其他线程影响。


首先我们看一下如何创建一个 java.lang.ThreadLocal 对象:


ThreadLocal<String> threadLocal = new ThreadLocal<>();
复制代码


创建方法需要声明泛型,下面是 ThreadLocal 的无参构造方法:


public ThreadLocal() {}
复制代码


该方法获取到的独享对象默认值是 null,如果你想设置其他默认值,可以使用以下语法:


ThreadLocal<String> threadLocal = new ThreadLocal<String>(){    @Override    protected String initialValue() {        return "Hello FunTester !";    }};
复制代码


亦或者使用 java.lang.ThreadLocal#withInitial 方法创建 ThreadLocal 对象:


ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "Hello FunTester !");
复制代码


initialValue 方法内容如下:


public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {    return new SuppliedThreadLocal<>(supplier);}
复制代码


使用该方法会创建一个 ThreadLocal 子类 java.lang.ThreadLocal.SuppliedThreadLocal,该类重写了 java.lang.ThreadLocal.SuppliedThreadLocal#initialValue 方法,内容如下:


@Overrideprotected T initialValue() {    return supplier.get();}
复制代码


其次我们对于变量两个最基本的操作:获取和修改。


  • 获取:java.lang.ThreadLocal#get

  • 修改:java.lang.ThreadLocal#set


API 非常简单,这里需要注意的就是返回值和参数值的类型需要与 ThreadLocal 创建是定义的泛型类型保持一致。

2.8.2 最佳实战

在性能测试中,ThreadLocal 通常用来将共享对象转换成独享解决线程安全的问题。下面用一个案例演示 ThreadLocal 最佳实战:


package org.funtester.performance.books.chapter02.section8;
/** * ThreadLocal 演示 */public class ThreadLocalDemo {
public static void main(String[] args) { ThreadLocal<String> threadLocal = new ThreadLocal< String >() {// 创建一个线程本地变量 @Override protected String initialValue() { // 重写初始化方法 return "Hello FunTester !"; } }; for (int i = 0; i < 3; i++) { new Thread(() -> { // 创建并启动3个线程 System.out.println("初始值: " + threadLocal.get()); // 获取本地变量值,并输出 threadLocal.set(Thread.currentThread().getName() + " Hello FunTester !"); // 设置本地变量值 System.out.println("修改后值: " + threadLocal.get()); // 获取本地变量值,并输出 }).start(); } }
}
复制代码


控制台输出


初始值: Hello FunTester !修改后值: Thread-0    Hello FunTester !初始值: Hello FunTester !修改后值: Thread-1    Hello FunTester !初始值: Hello FunTester !修改后值: Thread-2    Hello FunTester !
复制代码


可以看到,每个线程打印的初始值都是一样的,重新赋值之后,每个线程打印的值都变得不一样了。这就说明每个线程实际获取的对象并不是同一个对象,也就实现了将共享对象转换成独享对象的设计思路,解决了线程安全的问题。

2.8.3 使用场景

除了以上使用场景外,java.lang.ThreadLocal 在性能测试使用的场景并不多。但在 ThreadLocal 使用过程中,需要注意潜在的内存泄漏问题。


如果 ThreadLocal 实例在某个类中定义为 static,而该类又被类加载器加载,那么这个 ThreadLocal 对象将一直存在于内存中,直到线程结束或者手动调用 remove()方法将其移除。


所以在性能测试中如果使用 ThreadLocal 来解决线程安全问题,需要对线程管理更加严格。对于新手而言,使用最佳实战中的方式是安全可靠的,若还是无法满足需求,则应该抛弃 ThreadLocal,寻求其他简单、可靠的解决方案。


书的名字:从 Java 开始做性能测试


如果本书内容对你有所帮助,希望各位不吝赞赏,让我可以贴补家用。赞赏两位数可以提前阅读未公开章节。我也会尝试制作本书的视频教程,包括必要的答疑。

发布于: 刚刚阅读数: 4
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
【连载 13】ThreadLocal 类_FunTester_InfoQ写作社区