ThreadLocal:看到这个单词,大家应该就猜到了,跟线程有关系。在讲之前我们先来看一段简单的代码:
public class ThreadLocalTest {
public static void main(String[] args) {
final Demo demo = new ThreadLocalTest().new Demo();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
demo.setNum(1);
try {
Thread.sleep(100);
}catch (Exception e){
}
demo.getNum("thread1");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
demo.setNum(2);
try {
Thread.sleep(100);
}catch (Exception e){
}
demo.getNum("thread2");
}
});
thread1.start();
thread2.start();
}
class Demo{
private int num;
public void setNum(int num){
this.num = num;
}
public void getNum(String name){
System.out.println(name+":num=="+num);
}
}
}
这个是模拟两个线程同时去操作一个共享变量,很容易看出来,返回的数据肯定是一致的,结果:
那么现在如果要求,每个线程打印出来的值一定是自己设置的值,我们可以怎么做呢?
那下面我们来修改一下这个代码:
public class ThreadLocalTest {
public static void main(String[] args) {
final Demo demo = new ThreadLocalTest().new Demo();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
demo.setNum(1);
try {
Thread.sleep(100);
}catch (Exception e){
}
demo.getNum("thread1");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
demo.setNum(2);
try {
Thread.sleep(100);
}catch (Exception e){
}
demo.getNum("thread2");
}
});
thread1.start();
thread2.start();
}
class Demo{
private ThreadLocal<Integer> num = new ThreadLocal<>();
public void setNum(int num){
this.num.set(num);
}
public void getNum(String name){
System.out.println(name+":num=="+num.get());
}
}
}
在这里我们把num定义为ThreadLocal对象,然后我们开一看一下执行结果:
从结果可以看出来,每个线程打印出来的结果都是当前本线程自己修改过的值,多个线程之间相互没有影响。
由此可以看出ThreadLocal就是使共享变量在每个线程中的操作相互不影响的作用。那么它是如何做到这一点的呢?我们下面来深入了解一下它的源码实现:
我们先来看一下它的set()方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
解析一下:1.首先我们要获取到当前操作的线程
2.获取当前线程的ThreadLocalMap类型的一个map对象,下面我们会说到这个ThreadLocalMap
3.如果map存在,这把当前的值放进去,可以看到这里key放置的是当前操作的ThreadLocal对象,值就是当前设置的值
4.如果map不存在,创建map并设置值
下面我们来看一下第二步:获取map的值
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
这里我们可以看见,传进来的参数是当前的线程对象T,这个map其实就是当前线程对象的变量:threadLocals
ThreadLocal.ThreadLocalMap threadLocals = null;
大家应该看到了,这个threadLocals就是ThreadLocal类里的ThreadLocalMap类型的一个对象。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
这里我们看到这里主要是给当前线程的threadlocals变量初始化,并且赋值。
下面我们看一下get()方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
解析一下:1.首先我们要获取到当前操作的线程
2.获取当前线程的ThreadLocalMap类型的一个map对象,下面我们会说到这个ThreadLocalMap
3.如果map存在,这把当前的值放进去,可以看到这里key放置的是当前操作的ThreadLocal对象,值就是当前设置的值
4.如果map不存在,创建map并设置值
我们看一下setInitialValue方法,这里是初始化了以个空值进去。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
总结:ThreadLocal为什么可以隔离数据,主要是因为他的内部是一个ThreadLocalMap(实现了Map功能),这里的key存储的是每一个线程,值是当前线程里设置的值,所以每个线程获取到的都是当前自己的数据,与其他线程是隔离的。