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