从ThreadLocal 的名字上可以看到,这是一个线程的局部变量。也就是说,只有当前线程可以访问。既然是只有当前线程可以访问的数据,自然是线程安全的。
实际上,ThreadLocal确实也是Thread的一个属性:
源码是这样的:
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
其中ThreadLocalMap 是ThreadLocal的内部类。
ThreadLocal有一个get()方法:它最后返回的是T值。
因为ThreadLocal是线程的属性,所以应该先获得当前线程,然后根据这个线程对象,获得ThreadLocalMap ,然后再使用这个map,根据键值也就是ThreadLocal对象,获得T值。因为一个线程可能有多个这样的局部变量。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
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);
}
还有一个初始化方法:
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;
}
这三个方法里面都有 TheadLocalMap,我们看下它的源码:
*/
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;
}
}
它里面的Entry是一个弱引用。引用有四种,强引用 软引用 弱引用 虚引用。
也就是说,ThreadLocal对ThreadLocalMap 中的K值的引用是一个弱引用,它的value是一个强引用。
在一个线程池中,线程可能不会被回收,那么线程引用ThreadLocal,就一直存在,但是可能以后都不再使用这个对象了,那么应该需要把这个对对象回收掉。我们可以通过设置
ThreadLocal t= null,此时ThreadLocalMap 的强引用就没有了,那么k就会被回收。但是vlaue不会被回收,这也就是ThreadLocal 造成内存泄漏的原因。
ThreadLocal中还有一个方法
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
当我们不使用这个对象了之后,就可以使用方法删除ThreadLocal。以避免内存泄漏。
下面是一个示例代码:
public class Lock1 {
static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<SimpleDateFormat>();
public String [] createStrings(){
Vector<String> v= new Vector<String>();
for(int i=0;i<100;i++){
v.add(Integer.toString(i));
}
return v.toArray(new String[]{});
}
public static class PareseDate implements Runnable {
int i = 0;
public PareseDate(int i) {
this.i = i;
}
public void run() {
try {
if (t1.get() == null) {
System.out.println("只会执行一次吧");
t1.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
Date t = t1.get().parse("2017-03-29 19:29:" + i % 60);
System.out.println(i + ":" + t);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String args[]) {
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
es.execute(new PareseDate(i));
}
es.shutdown();
}