threadLocal详细认识(使用场景与局限性)与样例测试

news2024/9/26 5:22:29

Threadlocal的介绍与使用

1,是什么?

ThreadLocal 是 Java 提供的一个工具类,用于在多线程环境中为每个线程提供独立的变量副本。它是 Java 标准库中的一部分,提供了线程局部存储的功能,这意味着每个线程都有自己独立的变量副本,这些副本在其他线程中不可见。


2,有啥特点?

线程隔离: ThreadLocal 确保每个线程都拥有自己的变量副本,线程之间的 ThreadLocal 变量相互独立。这使得 ThreadLocal 非常适合存储线程特定的数据。

简化多线程编程: ThreadLocal 可以避免使用复杂的同步机制,因为每个线程有自己的变量副本,不需要考虑线程安全的问题。

自动管理: ThreadLocal 提供了简单的方法来管理线程局部变量的生命周期,包括设置、获取和清除。


3,支持的类型?

支持所有类型,方便用户自定义存储对象


4,局限性

  • 使用不当容易造成内存泄露
  • 特性也是局限性,只能保证线程内的数据隔离
  • 测试困难
  • 不适合分布式环境

5,使用场景

  • 用户会话管理--没使用缓存的情况下,在同一访问中可能涉及不同权限的校验时,第一次从数据库获取权限信息,存入threadlocal,进行操作的第一次权限校验,在第二次权限校验的时候就可以直接从threadlocal获取信息。
  • 数据库连接管理--没使用全局数据库连接池的情况下,可用threadlocal存入连接对象,避免同一访问中多次创建删除连接
  • 性能优化--如上所述,threadlocal可在一些关键场景缓存对象或数据,避免重复的创建和销毁

6,使用注意

不要将静态资源赋值给threadlocal,这会导致资源不再为线程独享而是共有

import java.util.HashMap;
import java.util.Map;

public class ThreadlocalTest {
    private static final ThreadLocal<Map<String,Object>> threadLocal =
            new ThreadLocal<>();

    private static Map<String, Object> threadLocalMap = new HashMap<>();

    static {
        threadLocal.set(threadLocalMap);
    }


7,工作原理

ThreadLocal 是 Java 提供的一个类,用于在多线程环境中为每个线程提供独立的变量副本。它的核心思想是每个线程都可以通过 ThreadLocal 存储和访问自己专有的数据副本,而这些数据副本对其他线程不可见
工作原理

7.1 线程局部存储:

ThreadLocal 使用 Thread 类中的 ThreadLocalMap 来存储每个线程的 ThreadLocal 变量。每个线程都有一个 ThreadLocalMap 实例,存储了该线程所有 ThreadLocal 变量的值。


7.2 存储和访问:

当调用 ThreadLocal 的 set() 方法时,ThreadLocal 将值存储到当前线程的 ThreadLocalMap 中。
当调用 get() 方法时,ThreadLocal 从当前线程的 ThreadLocalMap 中检索对应的值。

每个线程的 ThreadLocalMap 是私有的,线程之间的 ThreadLocalMap 不会相互影响。
ThreadLocal 的内部实现

7.3 ThreadLocal 类:

ThreadLocal 是一个泛型类,其内部使用 ThreadLocalMap 来存储线程局部变量。
ThreadLocal 维护一个 ThreadLocalMap,这是一个 Thread 类中的私有成员。ThreadLocalMap 是一个哈希表,用于保存每个 ThreadLocal 对象及其对应的值。

7.4 ThreadLocalMap 类:

ThreadLocalMap 是一个内嵌在 Thread 类中的静态类,用于存储 ThreadLocal 变量及其值。
ThreadLocalMap 的每个条目都是一个 Entry 对象,其中包含 ThreadLocal 对象的引用和对应的值。
键是 ThreadLocal 实例的引用,而值是存储在 ThreadLocal 中的数据。

static class ThreadLocalMap {
    static class Entry {
        final ThreadLocal<?> threadLocal;
        Object value;

        Entry(ThreadLocal<?> threadLocal, Object value) {
            this.threadLocal = threadLocal;
            this.value = value;
        }
    }

    private Entry[] table;
    // Other members and methods...
}

7.5 设置和获取值:

设置值:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
}


set() 方法通过 Thread 对象的 ThreadLocalMap 设置值。如果 ThreadLocalMap 尚不存在,则会创建一个新的 ThreadLocalMap 实例。

获取值:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T) e.value;
            return result;
        }
    }
    return initialValue();
}

get() 方法从 ThreadLocalMap 中获取值。如果 ThreadLocalMap 中没有该值,则调用 initialValue() 方法提供默认值。

7.6 清理:

移除值:

public void remove() {
    ThreadLocalMap map = getMap(Thread.currentThread());
    if (map != null) {
        map.remove(this);
    }
}


remove() 方法用于从 ThreadLocalMap 中移除当前线程的 ThreadLocal 变量,防止内存泄漏。

8,前后端分离项目中表现

在前后端分离的项目中,每个前端操作通常会触发后端的独立 HTTP 请求。每个请求由独立的线程处理,这意味着后端的每个 HTTP 请求都会在一个新的线程中处理。因此,ThreadLocal 变量在每个线程中都是隔离的。
每个请求独立

线程隔离: 在前后端分离的架构中,每次前端发起一个请求,后端会在一个新的线程中处理这个请求。每个请求线程都有自己的 ThreadLocal 变量副本。
请求处理: 每个 HTTP 请求处理的线程都拥有独立的 ThreadLocal 副本。不同的请求不会互相干扰,因为每个线程都具有独立的 ThreadLocal 变量。

ThreadLocal 有效范围:

单次请求有效: 在处理单个请求时,ThreadLocal 变量在该请求的线程内有效。在请求处理过程中,ThreadLocal 变量可以被设置和获取,但这些数据在请求完成后不会影响其他请求。
多次请求: 如果一个用户在同一页面上进行多个操作,每个操作都可能触发不同的 HTTP 请求。这些请求都是由不同的线程处理的,所以每个请求中的 ThreadLocal 数据是独立的。

9,使用注意
9.1 内存泄漏

长生命周期线程池: 在长生命周期的线程池(如应用服务器的线程池)中,ThreadLocal 可能导致内存泄漏。如果线程池中的线程持有 ThreadLocal 引用,而这些引用在任务完成后未被清理,可能会导致内存泄漏。
解决办法: 在使用 ThreadLocal 后,应在请求处理结束时调用 remove() 方法清理 ThreadLocal 变量,确保不留下对线程的强引用。

9.2 线程安全问题

局限性: ThreadLocal 只能保证线程内数据的隔离,不适合处理需要在多个线程间共享的数据。如果需要共享数据,应该使用同步机制或并发数据结构。

9.3 不适用于分布式环境


跨JVM: ThreadLocal 只在单个JVM内有效,不适用于跨JVM的分布式环境。如果需要跨JVM传递数据,应该考虑其他机制,如分布式缓存或数据库。

9.4 垃圾回收:

ThreadLocalMap 的 Entry 对象包含 ThreadLocal 对象的强引用。如果 ThreadLocal 对象被垃圾回收,但 ThreadLocalMap 中仍然持有对它的引用,则可能导致 ThreadLocal 的值也不会被回收。

10,实际代码与测试
 

threadlocal类

package com.rojer;

import java.util.HashMap;
import java.util.Map;

public class ThreadlocalTest {
    private static final ThreadLocal<Map<String, Object>> threadLocal =
            ThreadLocal.withInitial(HashMap::new);

    public static void setThreadLocalMap(String key, Object value) {
        threadLocal.get().put(key, value);
    }

    public static Object getThreadLocalMap(String key) {
        return threadLocal.get().get(key);
    }

    public static void removeThreadLocalMap(String key) {
        threadLocal.get().remove(key);
    }

    public static void clear() {
        threadLocal.get().clear();
    }
}

测试类

package com.rojer;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadLocalTestDemo {

    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 提交多个任务来测试 ThreadLocal
        for (int i = 0; i < 3; i++) {
            int threadId = i;
            executor.submit(() -> {
                // 设置线程本地变量的值
                ThreadlocalTest.setThreadLocalMap("key" + threadId, "value" + threadId);

                // 获取并打印线程本地变量的值
                Object value = ThreadlocalTest.getThreadLocalMap("key" + threadId);
                System.out.println("Thread " + threadId + " got value: " + value);

                // 清除线程本地变量
                ThreadlocalTest.removeThreadLocalMap("key" + threadId);
                Object removedValue = ThreadlocalTest.getThreadLocalMap("key" + threadId);
                System.out.println("Thread " + threadId + " after removal, got value: " + removedValue);
            });
        }

        // 关闭线程池
        executor.shutdown();
    }
}

测试结果(线程独享):

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1920236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【安全设备】APT攻击预警平台

一、什么是APT 高级持续性威胁&#xff08;APT&#xff09;是一种高度复杂和长期的网络攻击&#xff0c;旨在通过持续监视和访问特定目标来窃取敏感信息或进行其他恶意活动。这种攻击结合了多种先进的技术手段和社会工程学方法&#xff0c;以极高的隐蔽性实现长期潜伏和信息窃…

2 文件

2 文件 1、文件系统1.1 文件系统的逻辑结构1.2 文件的访问流程 2、文件类型3、文件的打开与关闭4、文件的内核结构5、文件的读写4.1 顺序与随机读写4.2 文件描述符的复制4.3 访问测试4.4 修改文件大小 5、文件锁5.1 读写冲突5.2 文件锁5.3 文件锁的内核结构 6、文件的元数据7、…

MTK Camera 冷启动、前后摄切换性能优化分析

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、背景二、问题分解三、工具分析四、 traceView教程五、surface create优化六、systrace的教程七、优化方案八、前后切换速度优化九、优化方案十、热…

旷野之间4 - 100 个 Kubernetes 面试问题及答案

100 个 Kubernetes 面试问题及答案 Kubernetes 简介 什么是 Kubernetes&#xff1f; Kubernetes 是一个开源容器编排平台&#xff0c;可自动部署、扩展和管理容器化应用程序。 什么是容器&#xff1f; 容器是一个轻量级、独立的、可执行软件包&#xff0c;其中包含运行应用…

学习笔记——动态路由——IS-IS中间系统到中间系统(特性之路由撤销)

6、路由撤销 ISIS路由协议的路由信息是封装在LSP报文中的TLV中的&#xff0c;但是它对撤销路由的处理和OSPF的处理方式类似。 在ISIS中撤销一条路由实则是将接口下的ISIS关闭&#xff1a; 撤销内部路由&#xff1a; 在ISIS中路由信息是由IP接口TLV和IP内部可达性TLV共同来描…

游戏AI的创造思路-技术基础-决策树(2)

上一篇写了决策树的基础概念和一些简单例子&#xff0c;本篇将着重在实际案例上进行说明 目录 8. 决策树应用的实际例子 8.1. 方法和过程 8.1.1. 定义行为 8.1.2. 确定属性 8.1.3. 构建决策树 8.1.4. 实施行为 8.1.5. 实时更新 8.2. Python代码 8. 决策树应用的实际例子…

hudi数据湖万字全方位教程+应用示例

1、时间轴&#xff08;TimeLine&#xff09; Hudi的核心是维护表上在不同的即时时间&#xff08;instants&#xff09;执行的所有操作的时间轴&#xff08;timeline&#xff09;&#xff0c;这有助于提供表的即时视图 一个instant由以下三个部分组成&#xff1a; 1&#xff09;…

YOLOv10改进 | Conv篇 | RCS-OSA替换C2f实现暴力涨点(减少通道的空间对象注意力机制)

一、本文介绍 本文给大家带来的改进机制是RCS-YOLO提出的RCS-OSA模块&#xff0c;其全称是"Reduced Channel Spatial Object Attention"&#xff0c;意即"减少通道的空间对象注意力"。这个模块的主要功能是通过减少特征图的通道数量&#xff0c;同时关注空…

Android使用AndServer在安卓设备上搭建服务端(Java)(Kotlin)两种写法

一直都是通过OkHttp远程服务端进行数据交互&#xff0c;突发奇想能不能也通过OkHttp在局域网的情况下对两个安卓设备或者手机进行数据交互呢&#xff1f; 这样一方安卓设备要当做服务端与另一个安卓设备通过OkHttp进行数据交互即可 当然还可以通过 socket 和 ServerSocket 通…

IC后端设计中的shrink系数设置方法

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 在一些成熟的工艺节点通过shrink的方式(光照过程中缩小特征尺寸比例)得到了半节点,比如40nm从45nm shrink得到,28nm从32nm shrink得到,由于半节点的性能更优异,成本又低,漏电等不利因素也可以…

旷野之间5 - AI基础代理决策的范式转变

介绍 让我们来谈谈最近在人工智能领域引起轰动的一件事——基础代理及其彻底改变我们所知的决策的潜力。现在,我知道你可能会想,“另一天,又一个人工智能突破,乏味无趣。”但相信我,这是一个改变游戏规则的突破,值得你关注。 如果您一直在关注人工智能和人工智能代理的…

JupyterNotebook中导出当前环境,并存储为requirements.txt

​使用Anaconda管理Python环境时&#xff0c;可以轻松地导出环境配置&#xff0c;以便在其他机器或环境中重新创建相同的环境。可以通过生成一个environment.yml文件实现的&#xff0c;该文件包含了环境中安装的所有包及其版本。但是&#xff0c;常常在一些课程中JupyterNotebo…

synchronized关键字详解(全面分析)

目录 synchronized关键字详解1、synchronized关键字简介2、synchronized作用和使用场景作用使用场景①、用在代码块上(类级别同步)②、用在代码块上(对象级别同步)③、用在普通方法上(对象级别同步)④、用在静态方法上(类级别同步)总结&#xff1a; 3、synchronized底层原理&am…

记录些Redis题集(1)

为什么Redis要有淘汰机制&#xff1f; 淘汰机制的存在是必要的&#xff0c;因为Redis是一种基于内存的数据库&#xff0c;所有数据都存储在内存中。然而&#xff0c;内存资源是有限的。在Redis的配置文件redis.conf中&#xff0c;有一个关键的配置项&#xff1a; # maxmemory…

vue3<script setup>自定义指令

main.ts // 自定义指令 app.directive(color,(el,binding) > {el.style.color binding.value })这段代码定义了一个名为color的自定义指令&#xff0c;并将其注册到Vue应用实例app上。自定义指令接收两个参数&#xff1a;el和binding。el是绑定指令的元素&#xff0c;而bi…

240711_昇思学习打卡-Day23-LSTM+CRF序列标注(2)

240711_昇思学习打卡-Day23-LSTMCRF序列标注&#xff08;2&#xff09; 今天记录LSTMCRF序列标注的第二部分。仅作简单记录 Score计算 首先计算正确标签序列所对应的得分&#xff0c;这里需要注意&#xff0c;除了转移概率矩阵&#x1d40f;外&#xff0c;还需要维护两个大小…

解决鸿蒙开发中克隆项目无法签名问题

文章目录 问题描述问题分析解决方案 问题描述 在一个风和日丽的早晨&#xff0c;这是我学习鸿蒙开发的第四天&#xff0c;把文档过了一遍的我准备看看别人的项目学习一下&#xff0c;于是就用git去clone了一个大佬的开源项目&#xff0c;在签名的时候遇到了问题&#xff1a; h…

Codeforces Round 957 (Div. 3)(A~E题解)

这次比赛只能用抽象来形容&#xff0c;前五道题都没有什么算法&#xff0c;都是思维加模拟都能过&#xff0c;然后第四题卡住了&#xff0c;第五题不知道为什么做出来的人那么少&#xff0c;就是纯暴力就能过&#xff0c;但是没抓住上分的机会&#xff0c;有些可惜&#xff0c;…

Pytorch(笔记8神经网络nn)

1、nn.Module torch.nn是专门为深度学习而设计的模块。torch.nn的核心数据结构是Module&#xff0c;它是一个抽象的概念&#xff0c;既可以表示神经网络中的某个层&#xff08;layer&#xff09;&#xff0c;也可以表示一个包含很多层的神经网络。在实际使用中&#xff0c;最常…

可视化学习:如何用WebGL绘制3D物体

在之前的文章中&#xff0c;我们使用WebGL绘制了很多二维的图形和图像&#xff0c;在学习2D绘图的时候&#xff0c;我们提过很多次关于GPU的高效渲染&#xff0c;但是2D图形的绘制只展示了WebGL部分的能力&#xff0c;WebGL更强大的地方在于&#xff0c;它可以绘制各种3D图形&a…