一,简介和应用
Java中对线程局部存储进行支持的类是:
public class ThreadLocal<T> extends Object
其文档解释为:
该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
解释下这段话:
1,该类提供“线程局部存储技术”。即尽管表面上,访问的都是这个类的get()方法和set()方法,但是不同的线程设置的和获取的数据是不同的。
2,该类往往是作为其他类的private static字段存在,用以保持一个与线程本身有关的变量的。
方法摘要 | |
---|---|
T | get() 返回此线程局部变量的当前线程副本中的值。 |
protected T | initialValue() 返回此线程局部变量的当前线程的“初始值”。 |
void | remove() 移除此线程局部变量当前线程的值。 |
void | set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。 |
例如,任务类Task被作为线程池的任务执行,线程池的线程执行任务需要统计执行的线程数,因此,这个数可以作为线程局部存储的数据。
public class Task implements Runnable{
private static final ThreadLocal threadLocal = new ThreadLocal();
public void run() {
Integer in = (Integer)threadLocal.get();
if(in == null){
//线程里没有该数据,说明是第一次执行
threadLocal.set(new Integer(1));
}else{
//线程中有该数据,则应该进行更新
threadLocal.set(in+1);
}
//执行任务的时候打印下目前已经有多少任务是由该线程执行的
System.out.println(threadLocal.get().toString()+" tasks are run by this thread. ");
}
}
二,实现原理
这种机制的实现其实很简单。类ThreadLocal中有个内部类ThreadLocalMap 其是 ThreadLocal 和 所设定的值的Key-Value键值对。 例如对于上述例子,第一次则对应的为 threadLocal -- Integer(1)。但是该ThreadLocalMap的实例,则存储于线程中,是线程类的成员变量。
因此
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- return (T)map.get(this);
- // Maps are constructed lazily. if the map for this thread
- // doesn't exist, create it, with this ThreadLocal and its
- // initial value as its only entry.
- T value = initialValue();
- createMap(t, value);
- return value;
- }
-
- ThreadLocalMap getMap(Thread t) {
- return t.threadLocals;
- }
如上源代码所示,get()方法先调用getMap(Thread t)方法获取线程的成员变量threadLocals,然后从这个threadLocals中以本ThreadLocal 实例为参数获取其值。
对应的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);
- }
因此,实际上这个类都是从线程的成员变量中获取自己这个类的实例对应的值的。因此尽管类的参数相同,都是自己,但是不同线程导致数据来源不同,因此设置和获取都是线程自己的成员变量。