首页 > 其他分享 >threadLocal详细认识(使用场景与局限性)与样例测试

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

时间:2024-07-11 21:59:06浏览次数:17  
标签:请求 ThreadLocalMap 样例 ThreadLocal value threadLocal 局限性 线程

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();
    }
}

测试结果(线程独享):

标签:请求,ThreadLocalMap,样例,ThreadLocal,value,threadLocal,局限性,线程
From: https://blog.csdn.net/weixin_54925172/article/details/140361219

相关文章

  • Java 并发 - ThreadLocal详解
    ThreadLocal是通过线程隔离的方式防止任务在共享资源上产生冲突,线程本地存储是一种自动化机制,可以为使用相同变量的每个不同线程都创建不同的存储。@立刀旁目录#带着BAT大厂的面试问题去理解#ThreadLocal简介#ThreadLocal理解#ThreadLocal原理#如何实现线程隔......
  • ThreadLocal 源码浅析
    前言多线程在访问同一个共享变量时很可能会出现并发问题,特别是在多线程对共享变量进行写入时,那么除了加锁还有其他方法避免并发问题吗?本文将详细讲解ThreadLocal的使用及其源码。一、什么是ThreadLocal?ThreadLocal是JDK包提供的,它提供了线程本地变量,也就是说,如果你创建......
  • ThreadLocal 源码浅析
    前言多线程在访问同一个共享变量时很可能会出现并发问题,特别是在多线程对共享变量进入写入时,那么除了加锁还有其他方法避免并发问题吗?本文将详细讲解ThreadLocal的使用及其源码。一、什么是ThreadLocal?ThreadLocal是JDK包提供的,它提供了线程本地变量,也就是说,如果你......
  • ThreadLocal详解
    在做项目时发现项目中一般都会把用户信息存入ThreadLocal中,方便后续使用用户信息。但是ThreadLocal的原理是什么呢?这里结合网上的资料记录一下我自己的理解。ThreadLocal是什么?网上有的说法是ThreadLocal是线程本地变量,如果创建了一个ThreadLocal变量,那么访问这个变量的每......
  • ThreadLocal 核心源码分析
    ThreadLocal简介多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的一种保证和规避多线程访问出......
  • 深入理解Java中的StringBuffer与StringBuilder:性能、用法与代码样例
    在Java编程中,当我们需要频繁地修改字符串时,使用String类可能会遇到性能问题,因为String是不可变的(immutable)。为了解决这个问题,Java提供了两个可变字符串类:StringBuffer和StringBuilder。这两个类都允许我们在不创建新对象的情况下修改字符串,但它们之间也有一些重要的区别。......
  • 在 Wed 中应用 MyBatis(同时使用MVC架构模式,以及ThreadLocal 事务控制)
    1.在Wed中应用MyBatis(同时使用MVC架构模式,以及ThreadLocal事务控制)@目录1.在Wed中应用MyBatis(同时使用MVC架构模式,以及ThreadLocal事务控制)2.实现步骤:1.第一步:环境搭建2.第二步:前端页面index.html3.第三步:创建pojo包、service包、dao包、web包、utils包,exceptions......
  • ThreadLocal源码分析
    目录0x00ThreadLocal0x01ThreadLocalMap0x02ThreadLocal内存泄漏0x00ThreadLocalThreadLocal提供了线程局部的变量,但和普通局部变量不同,同一个ThreadLocal变量可以被多个线程共享,而不是线程私有的。在ThreadLocal源代码中有一个使用例子,代码如下:importjava.ut......
  • 【java】JVM前端编译器的局限性
    目录1.不涉及编译优化2.功能限制3.静态编译特性4.与AOT编译器的对比1.不涉及编译优化1.前端编译器的主要任务是将符合Java语法规范的Java代码转换为符合JVM规范的字节码文件2.并不会直接涉及编译优化等方面的技术。3.具体的优化细节通常是由HotSpot的JIT(Jus......
  • ERP发展历程四之 MRP II的局限性和与ERP的主要区别
    MRPⅡ理论的局限性MRPI思想的局限性主要表现在以下几个方面:(1)企业竞争范围的扩大,要求在企业的各个方面加强管理,并要求企业有更高的信息化集成,要求对企业的整体资源进行集成管理,而不仅仅只是对制造资源进行集成管理。现代企业都意识到,企业的竞争是综合实力的竞争,要求企业有......