从名称看,ThreadLocal是Thread和Local的组合,也就是说每一个Thread都拥有自己独立的一个Local变量副本。
package com.morris.jvm.threadlocal;
import java.util.concurrent.TimeUnit;
/**
* 演示ThreadLocal的使用
*/
public class SimpleThreadLocalDemo {
public static void main(String[] args) {
ThreadLocal<Person> threadLocal = new ThreadLocal<>();
new Thread(() -> {
call(threadLocal, "morris");
}, "t1").start();
new Thread(() -> {
call(threadLocal, "bob");
}, "t2").start();
}
private static void call(ThreadLocal<Person> threadLocal, String name) {
Person person = new Person();
person.name = name;
threadLocal.set(person);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
threadLocal.remove();
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
}
private static class Person {
String name;
@Override
public String toString() {
return "Person{name=" + name + "}";
}
}
}
运行结果如下:
t1: Person{name=morris}
t2: Person{name=bob}
t2: null
t1: null
从运行结果可以发现,每个线程都有一份自己的Person对象,互不影响,当调用remove()之后,数据就会被清空。
这里涉及到三个类,Thread类、ThreadLocal类以及ThreadLocal的静态内部类ThreadLocalMap。在Thread中有一个threadLocals变量,类型为ThreadLocal.ThreadLocalMap。
ThreadLocalMap可以简单理解为一个HashMap,其中key为ThreadLocal,value为调用ThreadLocal.set(T v)传入的值。
java.lang.ThreadLocal#set
public void set(T value) {
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); // key为ThreadLocal
else
createMap(t, value); // ThreadLocalMap不存在就创建
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; // ThreadLocal.ThreadLocalMap threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
调用ThreadLocal.set()方法会创建一个以当前ThreadLocal为key,传入的参数为value的键值对放入到当前线程的ThreadLocalMap中。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); // 从当前线程中取出ThreadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); // 根据key从map中取出value
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); // key不存在则设置一个初始值
}
调用ThreadLocal.get()方法会到当前线程的ThreadLocalMap中查找一个key为ThreadLocal的键值对所对应的值。
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
remove()方法很简单,就是从当前线程的ThreadLocalMap中移除key为ThreadLocal的键值对。
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;
}
protected T initialValue() {
return null;
}
在调用ThreadLocal.get()方法时,如果当前线程的ThreadLocalMap没有被创建,或者在这之前没有调用过set()方法,就会调用setInitialValue()方法返回一个初始值,默认为null,子类可以重写这个方法自定义初始值。
ThreadLocal中提供了一个工厂方法来更优雅的实现自定义初始值。
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
@Override
protected T initialValue() {
return supplier.get();
}
}
package com.morris.jvm.threadlocal;
/**
* 带自定义初始值的ThreadLocal的使用
*/
public class SuppliedThreadLocalDemo {
public static void main(String[] args) {
ThreadLocal<String> threadLocal = ThreadLocal.withInitial(()->"hello");
new Thread(() -> {
threadLocal.set("morris");
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
threadLocal.remove();
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
}, "t1").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
}, "t2").start();
}
}
运行结果如下:
t1: morris
t1: hello
t2: hello
值得注意的是,当调用remove()之后,再调用get()返回的是初始值而不是null,这点从源码中也可以分析得出。
更多精彩内容关注本人公众号:架构师升级之路