一、概述
ThreadLocal的名称比较容易让人误解,会认为其是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量。
其设计的初衷是为了解决多线程编程中的资源共享问题。提起这个,大家一般会想到synchronized,synchronized采取的是“以时间换空间”的策略,本质上是对关键资源上锁,让大家排队操作。而ThreadLocal采取的是“以空间换时间”的思路,为每个使用该变量的线程提供独立的变量副本,在本线程内部,它相当于一个“全局变量”,可以保证本线程任何时间操纵的都是同一个对象。
二、实例
下面用一个实例阐述ThreadLocal的使用方法
1. 创建一个Context类,其中含有transactionId属性。
2. 创建MyThreadLocal做为容器,将一个Context对象保存于ThreadLocal中
3.模拟业务层,在某处读取context对象
4.多线程客户端程序,用于测试
package ThreadLocalTest;
/**
* Created by majian on 2018/7/5.
* 创建一个Context类,其中含有transactionId属性。
*/
public class Context {
private StringtransactionId =null;
public String getTransactionId() {
return transactionId;
}
public void setTransactionId(String transactionId) {
this.transactionId = transactionId;
}
}
package ThreadLocalTest;
/**
* Created by majian on 2018/7/5.
* 创建MyThreadLocal做为容器,将一个Context对象保存于ThreadLocal中
*/
public class MyThreadLocal {
public static final ThreadLocaluserThreadLocal =new ThreadLocal();
public static void set(Context user) {
userThreadLocal.set(user);
}
public static Context get() {
return (Context)userThreadLocal.get();
}
/* public static void unset() {
userThreadLocal.remove();
}*/
}
package ThreadLocalTest;
/**
* Created by majian on 2018/7/5.
*/
public class BusinessService {
public void businessMethod() {
Context context = MyThreadLocal.get();
System.out.println(context.getTransactionId());
}
}
package ThreadLocalTest;
import java.util.Random;
/**
* Created by majian on 2018/7/5.
* 多线程客户端程序,用于测试
*/
public class ThreadLocalDemoextends Thread {
public static void main(String[] args) {
Thread threadone =new ThreadLocalDemo();
threadone.start();
Thread threadtwo =new ThreadLocalDemo();
threadtwo.start();
}
@Override
public void run() {
Context context =new Context();
Random random =new Random();
int age= random.nextInt(100);
context.setTransactionId(String.valueOf(age));
System.out.println("set thread [" + getName() +"] contextid to " + String.valueOf(age));
// 在ThreadLocal中设置context
MyThreadLocal.set(context);
/* note that we are not explicitly passing the transaction id */
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
new BusinessService().businessMethod();
// MyThreadLocal.unset();
}
}
运行结果:
二. 深入分析ThreadLocal类
下面,我们来看一下 ThreadLocal 的具体实现,该类一共提供的四个方法:
publicT get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
其中,get()方法是用来获取 ThreadLocal变量 在当前线程中保存的值,set() 用来设置 ThreadLocal变量 在当前线程中的值,remove() 用来移除当前线程中相关 ThreadLocal变量,initialValue() 是一个 protected 方法,一般需要重写。
至此,可能大部分朋友已经明白了 ThreadLocal类 是如何为每个线程创建变量的副本的:
① 在每个线程Thread内部有一个 ThreadLocal.ThreadLocalMap 类型的成员变量 threadLocals,这个threadLocals就是用来存储实际的ThreadLocal变量副本的,键值为当前ThreadLocal变量,value为变量的副本(值);
② 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的值为value,存到 threadLocals;
③ 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在对应线程的threadLocals里面查找。