每个ThreadLocal实例是一个线程局部变量(Thread-Local Variables),这个局部变量为每个访问变量的线程提供单独的存储槽。你可以认为一个线程局部变量是一个多槽的变量,在它的每个线程里可以存储相同变量的不同值。每个线程只关注自己的值,和不关注其它线程它们自己的值在这变量中。
ThreadLocal的声明是ThreadLocal<T>,这个T是指示值的类型用于存储在这个变量中。这个类声明了如下的构造器和方法:
(1)ThreadLocal:创建一个新的线程局部变量。
(2) T get():在请求线程存储槽中返回值。当线程请求这个方法,而线程存储槽不存在,那么get()会去请求initialValue()方法。
(3)T initialValue():创建请求线程存储槽,和存储默认值在这个存储槽。这个初始值是null。你必须创建ThreadLocal的子类,并重写它的protected方法去提供更多有效的值。
(4)void remove():移除请求线程存储槽。如果这个方法调用get()却没使用set()。那么这个get()会调用initialValue()方法。
void set(T value):将值放入到请求线程存储槽。
Listing4-3展示了如何使用ThreadLocal去连接两个线程的不同用户IDs
package com.owen.thread.chapter4;
public class ThreadLocalDemo
{
private static volatile ThreadLocal<String> userID = new ThreadLocal<String>();
public static void main(String[] args)
{
Runnable r = new Runnable()
{
@Override
public void run()
{
String name = Thread.currentThread().getName();
if (name.equals("A"))
userID.set("foxtrot");
else
userID.set("charlie");
System.out.println(name + " " + userID.get());
}
};
Thread thdA = new Thread(r);
thdA.setName("A");
Thread thdB = new Thread(r);
thdB.setName("B");
thdA.start();
thdB.start();
}
}
之后实例化ThreadLocal和定义一个引用volatile的全局类命名为userID(这个全局是一个volatile因为它需要通过不同的线程,它可能在多处理器或多核机器上运行——用它代替final),默认主线程创建两个或多线程,它存储不同的java.lang.String.object在userID中,和输出它们的对象。
执行上面的代码,你将会看到:
A foxtrot
B charlie
值存储在线程局部变量中,而这些值之间没有联系。当一个新的线程被创建,它获取一个新的存储槽包含initialValue()的值。可能你需要涉及来自一个父线程(a parent thread)的值,一个线程创建另一个线程,给一个子线程,这个线程是创建过的。你可以用InheritableThreadLocal来完成任务。
InheritableThreadLocal是ThreadLocal的一个子类。下面也就是我们所知道的InheritableThreadLocal的构造器:
(1)T childValue(T parentValue):计算子类的初始值作为一个父值的应用,这个时候子线程被创建。在子线程被创建之前这个方法从父线程中请求。这个方法从parentValue中返回参数,和当其它值被要求时这个方法需要重写。
Listing4-4展示如何运用InheritableThreadLocal通过父线程的Integer对象给子线程。
Listing4-4通过父线程的对象给一个子线程。
package com.owen.thread.chapter4;
public class InheritableThreadLocalDemo
{
private static final InheritableThreadLocal<Integer> intVal = new InheritableThreadLocal<Integer>();
public static void main(String[] args)
{
Runnable rP = () -> {
intVal.set(new Integer(10));
Runnable rC = () -> {
Thread thd = Thread.currentThread();
String name = thd.getName();
System.out.printf("%s %d%n", name, intVal.get());
};
Thread thdChild = new Thread(rC);
thdChild.setName("Child");
thdChild.start();
};
new Thread(rP).start();
}
}
之后初始化InheritableThreadLocal和定义它为final类的变量(也可以用volatile替代)命名为intVal,这个默认线程创建一个父线程,它存储一个java.lang.Integer对象包含值为10在intVal变量中。这个父线程创建一个子线程,它调用intVal和接收它的父线程的Integer对象。执行上面代码你将会看到:Child 10。
源码下载:git@github.com:owenwilliam/Thread.git