ThreadLocal详解

ThreadLocal是Java提供的一个线程本地变量工具,它允许我们创建只能被同一个线程读写的变量。从线程的角度看,就好像是线程在访问自己的私有变量一样,避免了共享变量可能带来的并发问题。

ThreadLocal的工作原理可以简单概括为:

每个Thread对象内部维护了一个ThreadLocalMap
ThreadLocalMap是一个定制化的哈希表,其中key是ThreadLocal对象的弱引用,value是该线程存储的变量副本
当线程访问ThreadLocal变量时,实际是在访问自己的ThreadLocalMap中对应的条目

真正的持有关系:Thread 持有 ThreadLocalMap

在 Java 源码中,每个 Thread 对象内部都有一个专门存放线程私有变量的容器:

ThreadLocal.ThreadLocalMap threadLocals = null;

也就是说,ThreadLocalMap 是挂在 Thread 上的,每个线程独享一个 Map。当你调用 threadLocal.set(value) 时,代码会先获取当前线程 Thread.currentThread(),再拿到这个线程自己的 threadLocals,然后把数据存进去。数据从来不存在 ThreadLocal 对象本身里面。

ThreadLocal 的角色:只是 Key 和工具

既然 Map 在 Thread 里,ThreadLocal 负责什么呢?
它主要负责两件事:

  1. 作为 Key:在 ThreadLocalMap 的 Entry 中,Key 就是当前的 ThreadLocal 对象(实际是它的弱引用)。

  2. 提供操作入口:ThreadLocal 的 get/set/remove 方法,本质上都是在帮你找到当前线程的 Map,然后以自己(this)为 Key 去读写那个 Map。

所以,线程私有变量确实存储在 ThreadLocalMap 里,而这个 Map 是 Thread 的成员。ThreadLocal 只是那个用来在 Map 里定位你专属变量的“钥匙”

 

为什么容易产生“ThreadLocal 持有 Map”的错觉?

 

因为 ThreadLocalMap 被定义成了 ThreadLocal 的静态内部类。从类结构上看,ThreadLocalMap 属于 ThreadLocal 的一部分,所以容易让人误以为 ThreadLocal 实例持有它。但实际上,运行时真正的持有者是 Thread,ThreadLocal 类只是提供了 Map 的定义和操作逻辑

 


==================================================== 

package com.wsh.mybatis.mybatisdemo.entity;

public class ThreadLocalDemo {

private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

public static void main(String[] args) {
new Thread(() -> {
//设置t1线程中本地变量的值
threadLocal.set("t1线程");
//获取t1线程中本地变量的值
System.out.println("t1线程局部变量的value : " + threadLocal.get());
}, "t1").start();

new Thread(() -> {
//设置t2线程中本地变量的值
threadLocal.set("t2线程");
//获取t1线程中本地变量的值
System.out.println("t2线程局部变量的value : " + threadLocal.get());
}, "t2").start();
}
}
 

======

ThreadLocal<Map<String, Object>> context = new ThreadLocal<>();

Map<String, Object> data = new HashMap<>();
data.put("userName", "张三");
data.put("userId", 1001);
context.set(data);

Map<String, Object> map = context.get();
System.out.println(map.get("userName"));
 ====================================

 

4fc0a50d09eaa53f3a16eca5dd8fbcd8_doh4ooffebbpi_ce1f3c96052d410b9599c14b2f1a35bc_x-oss-process=image%2Fresize%2Cw_1400%2Cm_lfit%2Fformat%2Cwebp

 从上图我们可以发现Thread 中持有一个ThreadLocalMap ,这里你可以简单理解为就是持有一个数组,这个数组是Entry 类型的。 Entry 的key 是ThreadLocal 类型的,value 是Object 类型。也就是一个ThreadLocalMap 可以持有多个ThreadLocal。

每个线程中都有一个ThreadLocalMap数据结构 ,当执行set方法时,其值是保存在当前线程的threadLocals变量中,当执行set方法中,是从当前线程的threadLocals变量获取。

所以在线程1中set的值,对线程2来说是摸不到的,而且在线程2中重新set的话,也不会影响到线程1中的值,保证了线程之间不会相互干扰。

 

 

 l==========ThreadLocal 存储自己的内部变量方法==================

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class ThreadLocalDemo {

/**
* 每个线程独立的 ThreadLocal 变量
*/
private static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "默认值(未设置)";
}
};

/**
* 线程ID -> 该线程私有变量值,便于按线程ID查询
*/
private static final ConcurrentHashMap<Long, String> THREAD_DATA_MAP = new ConcurrentHashMap<>();

/**
* 封装线程任务,支持传入自变量 data
*/
static class WorkerTask implements Runnable {
private final String data; // 外部传入的变量

public WorkerTask(String data) {
this.data = data;
}

@Override
public void run() {
long tid = Thread.currentThread().getId();

// 将传入的变量写入当前线程的 ThreadLocal
THREAD_LOCAL.set(data);
THREAD_DATA_MAP.put(tid, THREAD_LOCAL.get());

// 模拟业务处理,期间其他线程不会影响本线程的值
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}

System.out.println("线程[" + tid + "] 名称=" + Thread.currentThread().getName()
+ " ThreadLocal值=" + THREAD_LOCAL.get());

// 用完清理,防止内存泄漏(线程池场景尤其重要)
THREAD_LOCAL.remove();
}
}

public static void main(String[] args) throws InterruptedException {
System.out.println("主线程ID: " + Thread.currentThread().getId());

// 主线程设置自己的变量
THREAD_LOCAL.set("我是主线程的数据");
THREAD_DATA_MAP.put(Thread.currentThread().getId(), THREAD_LOCAL.get());
System.out.println("主线程 ThreadLocal 值: " + THREAD_LOCAL.get());

// 准备外部变量,分别传入不同线程
String[] inputs = {"线程-1 的私有数据", "线程-2 的私有数据", "线程-3 的私有数据"};

for (int i = 0; i < inputs.length; i++) {
// 将 inputs[i] 作为自变量传入线程任务
new Thread(new WorkerTask(inputs[i]), "Worker-" + (i + 1)).start();
}

// 等待所有子线程结束
TimeUnit.SECONDS.sleep(1);

// 按线程ID查询各自存储的变量
System.out.println("\n=== 按线程ID查询各自存储的变量 ===");
THREAD_DATA_MAP.forEach((tid, val) ->
System.out.println("线程ID=" + tid + " -> 变量值=" + val));

System.out.println("\n主线程最终 ThreadLocal 值: " + THREAD_LOCAL.get());

// 主线程清理
THREAD_LOCAL.remove();
}
}

 

========================================java 中线程的实现方式===================================

Java 中线程的实现方式主要有 4 种

1. 继承 Thread 类

class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程运行中: " + Thread.currentThread().getName());
}
}

public class Demo {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // 启动线程,调用 run()
}
}

 

2. 实现 Runnable 接口(推荐)

class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程运行中: " + Thread.currentThread().getName());
}
}

public class Demo {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();

// 或 Lambda 写法
new Thread(() -> System.out.println("Lambda 线程")).start();
}
}

 

3. 实现 Callable 接口 + FutureTask(可获取返回值

import java.util.concurrent.*;

public class Demo {
public static void main(String[] args) throws Exception {
Callable<Integer> task = () -> {
Thread.sleep(1000);
return 42;
};

FutureTask<Integer> futureTask = new FutureTask<>(task);
new Thread(futureTask).start();

Integer result = futureTask.get(); // 阻塞获取返回值
System.out.println("结果: " + result);
}
}

--------------------实现callable 接口需要实现call接口

import java.util.concurrent.*;

public class CallableDemo1 {
public static void main(String[] args) throws Exception {
// 1. 实现 Callable 接口(匿名内部类)
Callable<Integer> task = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("子线程开始计算: " + Thread.currentThread().getName());
Thread.sleep(1000);
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum; // 返回计算结果
}
};

// 2. 包装成 FutureTask
FutureTask<Integer> futureTask = new FutureTask<>(task);

// 3. 交给 Thread 启动
new Thread(futureTask, "计算线程").start();

System.out.println("主线程继续做其他事情...");

// 4. 获取返回值(阻塞直到计算完成)
Integer result = futureTask.get();
System.out.println("计算结果: " + result);
}
}

4. 使用线程池(ExecutorService)

import java.util.concurrent.*;

public class Demo {
public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(3);

pool.execute(() -> System.out.println("Runnable 任务"));

Future<String> future = pool.submit(() -> "Callable 结果");

pool.shutdown();
}
}

posted @ 2026-10-28 21:36  KLAPT  阅读(4)  评论(0)    收藏  举报