Threadlocal的简单使用和实现原理解析
ThreadLocal常用来在一个线程的作用范围内共享变量,它在多个线程之间是相互独立的。
一、ThreadLocal的使用
下边的代码先声明了一个公共的ThreadLocal,泛型是string表示这个threadLocal中只能存string.
在线程t1中调用set方法给local赋值t1,然后线程t1分别去调用方法m1,m2,在m1,m2中用get方法获取local
中的值,在线程t2中也进行同样的操作。
然后就会得到这样的输出:t1线程调用方法时拿到的是t1自己设置的值,t2也是
t1-m2中获取local:t1
t1-m1中获取local:t1
t2-m2中获取local:t2
t2-m1中获取local:t2
这说明了2个线程在调用m1、m2方法的时候获取到的是自己的threadlocal中维护的值,互不干扰,所以threadlocal可以用来在一个线程范围内的多个方法间共享变量。
public class ThreadLocalTest {
public static ThreadLocal<String> local = new ThreadLocal<>();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
local.set("t1");
m1();
m2();
}
},"t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
local.set("t2");
m1();
m2();
}
},"t2");
t2.start();
}
public static void m1(){
System.out.println(Thread.currentThread().getName()+"-m2中获取local:"+local.get());
}
public static void m2(){
System.out.println(Thread.currentThread().getName()+"-m1中获取local:"+local.get());
}
threadlocal就是这样使用的,先定义一个公共的变量,然后在线程中先设置值再获取值,使用这种方式可以减少通过方法传参来传递参数。
二、ThreadLocal的实现原理解析
ThreadLocal为什么可以实现线程独立呢,那是因为每个线程都维护自己的threadlocal,Thread类中有一个属性threadLocals
,这个属性中会保存自己的变量值。
可以用一幅图来描述下上边的代码对应的堆内存。
由这张图可以看出来threadlocal为什么可以做到线程独立,因为每个线程对象中都有一个threadLocals
属性,而ThreadLocal其实就相当于是一个工具类,set/get方法用来操作线程对象中的threadLocals
属性,因为每个thread对象是独立的所以其属性也是独立的,就不会相互影响。
然后来简单看下ThreadLocal的源码,主要看下set/get方法
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);
}
get方法
ublic 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;
}
}
return setInitialValue();
}
可以看到和上面的图是一致的。
三、子线程中能否获取到父线程中set的ThreadLocal值
如果使用的是ThreadLocal,子线程是不能访问的,因为线程对象(Thread)的threadLocals
属性不会传递给子线程。
但Thread类中有另一个属性是会传递给子线程的
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
那么子线程就没办法和父线程共享ThreadLocal变量了吗?其实是可以的
ThreadLocal有一个子类InheritableThreadLocal
就是用来操作线程对象的这个属性的,所以如果父线程中是给这个类对象set值,在子线程中是可以访问到的。