目录
先说原理:
再上代码:
运行结果:
先说原理:
ThreadLocal
是一个本地线程副本变量工具类,它可以在每个线程中创建一个副本变量,每个线程可以独立地修改自己的副本变量,而不会影响其他线程的副本变量。它的实现原理可以简单概括如下:
ThreadLocal
内部维护了一个Map
对象,用于存储每个线程的副本变量。Map
的键为线程对象,值为对应线程的副本变量。- 在每个线程中,
ThreadLocal
实例会创建一个唯一的ThreadLocalMap
对象,用于存储该线程的所有副本变量。ThreadLocalMap
是一个自定义的哈希表数据结构,继承自WeakReference
,它的键为ThreadLocal
对象,值为对应线程的副本变量。 - 在创建
ThreadLocal
实例时,实际上是在当前线程的ThreadLocalMap
中新增一个键值对,其中键为当前ThreadLocal
对象,值为初始化的副本变量。 - 当需要获取当前线程的副本变量时,
ThreadLocal
实例会先获取当前线程的ThreadLocalMap
,再通过当前ThreadLocal
对象作为键来获取对应的副本变量。由于每个线程独立维护自己的ThreadLocalMap
,所以不同线程的相同ThreadLocal
对象对应的副本变量也是不同的。 - 当一个线程结束时,它持有的所有
ThreadLocalMap
中的键值对会成为垃圾对象,但由于ThreadLocalMap
的键是WeakReference
类型,所以这些键可能被垃圾回收器回收,但值对象不会被回收,从而导致内存泄漏。为了解决这个问题,ThreadLocal
内部使用了ThreadLocalMap
的expungeStaleEntry()
方法,定期清除废弃的键值对。
总的来说,ThreadLocal
通过维护一个 Map
,为每个线程创建一个独立的 ThreadLocalMap
,并使用弱引用来避免内存泄漏,从而实现了在每个线程中创建独立的副本变量,并提供了线程安全的访问方式。
再上代码:
package cn.net.cdsz.ccb.test;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
public class test {
static class ThreadA implements Runnable {
private ThreadLocal<String> threadLocal;
public ThreadA(ThreadLocal<String> threadLocal) {
this.threadLocal = threadLocal;
}
@Override
public void run() {
threadLocal.set("A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadA输出:" + threadLocal.get());
}
}
static class ThreadB implements Runnable {
private ThreadLocal<String> threadLocal;
public ThreadB(ThreadLocal<String> threadLocal) {
this.threadLocal = threadLocal;
}
@Override
public void run() {
threadLocal.set("B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadB输出:" + threadLocal.get());
}
}
public static void main(String[] args) {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
new Thread(new ThreadA(threadLocal)).start();
new Thread(new ThreadB(threadLocal)).start();
}
}
运行结果:
最常⻅的ThreadLocal使⽤场景为⽤来解决数据库连接、Session管理等。数据库连
接和Session管理涉及多个复杂对象的初始化和关闭。如果在每个线程中声明⼀些
私有变量来进⾏操作,那这个线程就变得不那么“轻量”了,需要频繁的创建和关闭
连接。