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 负责什么呢?
它主要负责两件事:
-
作为 Key:在 ThreadLocalMap 的
Entry中,Key 就是当前的 ThreadLocal 对象(实际是它的弱引用)。 -
提供操作入口: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"));
====================================

从上图我们可以发现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();
}
}

浙公网安备 33010602011771号