多线程访问同一个共享变量时,容易出现并发冲突,为了保证线程的安全,一般使用者在访问共享变量时,需要进行适量的同步。而ThreadLocal
提供了线程的私有变量,每个线程都可以通过set()
和get()
来对这个私有变量进行操作,但不会和其他线程的私有变量进行冲突,实现了线程的数据隔离。InheritableThreadLocal
作用和ThreadLocal
相同,同时增加了一个功能,可以共享父线程InheritableThreadLocal
提供的私有部变量,下面从源码角度,分别介绍这两个类。
下面是TheadLocal一段简单使用的代码。线程a、线程b内部都维护了一个String类型变量,通过ThreadLocal 变量thl的get、set方法可以对其进行访问和修改。
public class Main{
static ThreadLocal thl = new ThreadLocal();
static void print(){
System.out.println(thl.get());
}
public static void main(String args[]) throws Exception{
Thread a = new Thread(new Runnable(){
@Override
public void run() {
thl.set("ThreadA variable");
print();
}
});
Thread b = new Thread(new Runnable(){
@Override
public void run() {
thl.set("ThreadB variable");
print();
}
});
a.start(); b.start();
a.join(); b.join();
}
}
//结果可能是 ThreadA variable ThreadB variable
//或者是 ThreadB variable ThreadA variable
get、set方法
每个线程里面均维护有threadLocals和inheritableLocals两个变量,两个变量均为ThreadLocalMap类(类似HashMap),调用ThreadLocal的set、get方法,其实就是对threadLocalMap进行修改和访问操作。同时也就解释了ThreadLocal提供的变量是线程私有的原因。
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取线程的ThreadLocalMap变量
ThreadLocalMap map = getMap(t);
//key为ThreadLocal变量、value为T变量,添加进ThreadLocalMap变量
if(map != null) map.set(this, value);
else createMap(t,value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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;
}
}
}
InheritableThreadLocal
下面这段代码,说明ThreadLocal
不支持继承性
public class TestThreadLocal{
public static ThreadLocal threadLocal = new ThreadLocal();
public static void main(String[] args) throws Exception {
threadLocal.set("Hello World");
Thread t = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("t:" + threadLocal.get());
}
});
t.start(); t.join();
System.out.println("Main:" + threadLocal.get());
}
}
//t:null
//Main:Hello World
而相比于ThreadLocal
、InheritableThreadLocal
恰如其名称所描述的支持,子线程共享父线程InheritableThreadLocal
提供的私有变量。
下面解释InheritableThreadLocal的共享原理,线程在初始化的时候,如果父线程的inheritableThreadLocals
不为空,则会调用createInheritedMap
函数, 最终调用ThreadLocalMap的一个私有构造函数,用于将父线程的InheritableThreadLocal变量移植(直接复制引用,并没有重新开辟内存空间)到子线程的InheritableThreadLocal变量,达到一个变量共享的目的。
//线程初始化函数
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AcessControlContext acc) {
Thread parent = currentThread();
if(parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
tid = nextThreadID();
}
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap){
return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
for(int j = 0; j < len; j++) {
Entry e = parentTable[j];
if(e != null) {
@SuppressWarnings("unchecked")
ThreadLocal key = (ThreadLocal) e.get();
if(key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len -1);
while(table[h] != null) {
h = nextIndex(h, len);
}
table[h] = e;
size++;
}
}
}
}
参考资料
Java并发编程之美