ThreadLocal类在java中能够让变量只能被相同的线程读写.那么即使两个线程同时访问相同的代码中的相同变量,也会产生两个变量副本,变量副本分别仅对本线程可见.
在java中较为典型的应用莫过于Servlet的HttpServletRequest了.
创建一个ThreadLocal
done like this:
private ThreadLocal myThreadLocal = new ThreadLocal();
以上代码实例化了一个ThreadLocal, ThreadLocal在同一个线程中,仅需要被实例化一次即可.多个线程运行这段代码时,每个线程都会创建一个自己的ThreadLocal副本,且仅对本线程可见.当两个线程往ThreadLocal设置不同值时,它们见不到对方设置的值.
访问ThreadLocal
ThreadLocal创建完成后,可以通过set()来存储值.
done like this:
myThreadLocal.set("test var")
可以通过get()来取得原先存储的值.
done like this:
String myVar = (String)myThreadLocal.get();
调用get()返回之前存储的值,调用set()方法,可以传递一个Object作为存储值.
ThreadLocal泛型化
我们可以创建一个泛型化的ThreadLocal,这样在调用get()就不用进行类型强转了.
done like this:
private ThreadLocal myThreadLocal = new ThreadLocal<String>();
这样我们可以直接通过set()来存储目标类型值,通过get()来取得目标类型值:
myThreadLocal.set("text var");
String myVar = myThreadLocal.get();
设置ThreadLocal默认值
我们知道通过调用set()来存储值仅会对当前线程可见,对其他线程不可见.有时候我们需要设置对所有线程可见的默认值.
这时我们可以继承ThreadLocal类并覆盖initialValue().当然也可以用使用匿名内部类的方式.
done like this:
private ThreadLocal myThreadLocal = new ThreadLocal<String>() {
@Override protected String initialValue() {
return "This is the initial value";
}
};
现在所有线程在没有调用set()的前提下,都能通过get()来取得默认值.
完整实例
public class ThreadLocalDemo {
private ThreadLocal<Integer> myThreadLocal = new ThreadLocal();
private AtomicInteger counter = new AtomicInteger();
private Runnable runnable = () -> {
Thread curThread = Thread.currentThread();
int count = counter.getAndIncrement();
System.out.println(curThread.getName() + ": set the val(" + count + ")");
myThreadLocal.set(count);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(curThread.getName() + ": get the val(" + myThreadLocal.get() + ")");
};
public static void main(String[] args) {
ThreadLocalDemo tld = new ThreadLocalDemo();
IntStream.range(1, 3)
.mapToObj(i -> new Thread(tld.runnable, "Thread-" + i))
.map(Thread::new)
.forEach(Thread::start);
}
}
我们创建一个ThreadLocalDemo用于示范ThreadLocal的使用.首先我们在内部实例化了ThreadLocal用于存储int类型值,然后实例化AtomicInteger用于产生不同的整数,再然后就是定义我们的Runnable.Runnable中先后调用ThreadLocal的set()和get().set()和get()中间停顿了2s钟,用于预留时间让线程对ThreadLocal存储值进行覆盖,确保两个线程都已经调用ThreadLocal的set()后再调用get().预期结果是每个线程通过get()取得都是之前通过set()存储的值.
执行结果:
Thread-0: set the val(0)
Thread-1: set the val(1)
Thread-0: get the val(0)
Thread-1: get the val(1)
由结果可以看出ThreadLocal存储的值确实仅对本线程可见,若换成普通的变量结果应该是类似如下所示:
Thread-0: set the val(0)
Thread-1: set the val(1)
Thread-0: get the val(1)
Thread-1: get the val(1)
InheritableThreadLocal
InheritableThreadLocal是ThreadLocal的子类.与ThreadLocal不同的是InheritableThreadLocal存储值允许当前线程创建的所有子线程访问.
该系列博文为笔者复习基础所著译文或理解后的产物,复习原文来自Jakob Jenkov所著Java Concurrency and Multithreading Tutorial