之前曾转载过相关ThreadLocal的文章,但一直是处于迷糊状态,最近复习的时候偶然看到博客里的文章,所以对此类做一个深入的分析和总结。
(另外说明下,我这个源码的Java 版本是1.6.0_06)
在Java API 文档里,是这样解释的:
该类提供了线程局部 (thread-local) 变量。这些变量不同于它们的普通对应物,因为访问某个变量(通过其 get 或 set 方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例通常是类中的 private static 字段,它们希望将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。
这里有两个关键字,“线程局部变量”和“初始化副本”。这两个关键字基本涵盖了这个类的含义。
“线程局部变量”是指在同一个线程中,该变量是唯一的(可以把这个变量理解为同个线程内的单例)。
“初始化副本”是指在不同线程中,该变量是你定义的那个初始值(也就是那个副本)。
下面是Java API 的例子:
以下类生成对每个线程唯一的局部标识符(就是说在同一个线程内是不会出现两个相同的uniqueId的)。线程 ID 是在第一次调用 UniqueThreadIdGenerator.getCurrentThreadId() 时分配的,在后续调用中不会更改。
import java.util.concurrent.atomic.AtomicInteger;
public class UniqueThreadIdGenerator {
private static final AtomicInteger uniqueId = new AtomicInteger(0);
private static final ThreadLocal < Integer > uniqueNum =
new ThreadLocal < Integer > () {
//定义初始值(副本)
@Override protected Integer initialValue() {
return uniqueId.getAndIncrement();
}
};
public static int getCurrentThreadId() {
//API 上的例子是下面的,不过我以为应该是写错了,因为这样的话每次获得的uniqueId是不变的,都是0
// return uniqueId.get();
//我修改后的代码如下,就是把uniqueId换成uniqueNum
return uniqueNum.get();
}
} // UniqueThreadIdGenerator
下面是对ThreadLocal 类的源码分析下:
void set(Object value):设置当前线程的线程局部变量的值。
public Object get() :该方法返回当前线程所对应的线程局部变量。 (同一个线程获取同一个变量的引用)
public void remove() :将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
protected Object initialValue():返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的(在上面的例子也可以看到,其实就是一个默认值的设置)。这个方法是一个延迟调用方法,在线程第1次调用 get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
1、void set(Object value)方法分析
public void set(T value) {
Thread t = Thread.currentThread();
//查看当前线程中的ThreadLocalMap 是否存在。
ThreadLocalMap map = getMap(t);
//存在的话就把当前线程设置进ThreadLocalMap中。
if (map != null)
map.set(this, value);
else
//否则生成一个ThreadLocalMap 对象,并把值赋到当前线程的ThreadLocalMap中
createMap(t, value);
}
//-------------------------------解析的代码分割线-------------------------------
//返回当前线程的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//创建新的ThreadLocalMap赋值到当前线程
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
下面说下关于ThreadLocalMap 类,这个类有点类似于WeakHashMap(这个类对于解决某些问题提供了很好的性能上的方案,有空再继续深入了解。),虽然ThreadLocalMap没有继承Map的,但它具有Map的一些特性,有关这些特性就不说了,主要说说它们的一个很大的共同点就是,它们的key 都是弱引用的对象,因此在该键没有被引用时,将会被JVM收集。(有关弱引用的解析,请参考我的博客上的:http://zhxing.iteye.com/admin/blogs/571007)
ThreadLocalMap是ThreadLocal的一个嵌套类,它的一些源码如下:
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
2、 public Object get() 方法分析
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//这个是传入当前的ThreadLocal 对象引用获取所对应的值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
//如果map 对象为空,说明是第一次调用这个方法,之前也没有调用set()方法
return setInitialValue();
}
//-------------------------------解析的代码分割线-------------------------------
//这个方法很简单就是调用initialValue()获取初始值(默认值),而且要赋值在当前线程的map中(相当于调用了set()方法)
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;
}
//ThreadLocal 的默认实现是返回null 的,所以如果在多个线程中使用时,如果需要用到默认值,那子类就应该覆盖该值
protected T initialValue() {
return null;
}
3、public void remove() 方法
//这个方法没什么可说的,就是删除掉当前线程中的变量(释放内存)
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
总结
ThreadLocal 可以实现在同一个线程内共享一个变量,不同的线程不能共享,只能得到该变量的一个副本(就是一个默认的初始化值)。
在论坛中也有人提到ThreadLocal和synchronized 的问题,我觉得它们都是解决线程的两个不同的方案。ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题。
有关的讨论在:http://www.iteye.com/topic/179040
有关这个类的应用,可以参考http://zhxing.iteye.com/admin/blogs/306070