0 概述
维持线程封闭性的一种更规范的方法是使用ThreadLocal,这个类使得访问某个变量的每个线程都拥有自己的局部变量,它独立于初始化副本。本文主要介绍ThreadLocal相关知识。
1 类方法介绍
T get() 返回此线程局部变量的当前线程副本中的值。
protected T initialValue() 返回此线程局部变量的当前线程的“初始值”。
void remove() 移除此线程局部变量当前线程的值。
void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。
public class ThreadLocalTest {
//定义ThreadLocal对象
private static final ThreadLocal<Integer> LOCAL = new ThreadLocal<Integer>() {
//初始化值,每个线程都会获取到这个值得副本(注意引用对象)
@Override
protected Integer initialValue() {
return 0;
}
};
public static void main(String[] args) {
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
int num = LOCAL.get();
num++;
LOCAL.set(num);
System.out.println(Thread.currentThread().getName() + "num:" + LOCAL.get());
}
});
}
//启动线程
for (Thread thread : threads) {
thread.start();
}
}
}
结果如下,从结果可以看出线程之间是相互独立的
Thread-1num:1
Thread-0num:1
Thread-2num:1
Thread-3num:1
Thread-4num:1
Thread-5num:1
Thread-6num:1
Thread-7num:1
Thread-8num:1
Thread-9num:1
public class ThreadLocalTest {
private static Number number=new Number();
//定义ThreadLocal对象
private static final ThreadLocal<Number> LOCAL = new ThreadLocal<Number>() {
//初始化值,每个线程都会获取到这个值的副本(注意引用对象)
@Override
protected Number initialValue() {
return number;
}
};
public static void main(String[] args) {
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
Number num = LOCAL.get();
num.num++;
LOCAL.set(num);
System.out.println(Thread.currentThread().getName() + "num:" + LOCAL.get().num);
}
});
}
//启动线程
for (Thread thread : threads) {
thread.start();
}
}
private static class Number
{
private int num=0;
}
}
由于每个线程得到初始化变量副本是Number对象引用,所以输出结果是不可预期的.
Thread-2num:3
Thread-3num:4
Thread-0num:3
Thread-1num:3
Thread-5num:6
Thread-4num:5
Thread-6num:7
Thread-7num:8
Thread-8num:9
Thread-9num:10
2 实现原理
- 每个线程的变量副本存储在哪里?
变量的副本是怎么从共享变量赋值出来的?
JDK 源码分析:
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取Thread类 对象成员变量threadLocals
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//创建一个ThreadLocalMap的map,这个map的key是ThreadLocal本身
createMap(t, value);
}
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;
}
}
//如果map为空返回初始化值
return setInitialValue();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
从源码可以每一个线程维护了一个独立的ThreadLocalMap映射表,key是ThreadLocal本身,Value是存储的对象,从而实现了线程之间的相互独立。
3 应用场景
ThreadLocal用于存放多线程中线程上下文信息,保存引用类型数据,避免引用类型的传递。如维持一个数据库的全局连接对象,从而避免调用每个方法的时候都要传递一个连接对象~