可参考《实战Java高并发程序设计》4.3
ThreadLocal是一个线程的局部变量,只有当前线程可以访问的数据。所以是线程安全的。
为每一个线程分配不同的对象,需要在应用层面保证。ThreadLocal只是起到了简单容器的作用。如果在应用上为每一个线程分配了相同的对象,ThreadLocal也不能保证线程安全。
/**
*ThreadLocal简单使用
*/
public class TestTheadLocal {
static ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>();
public static class ParseDate implements Runnable {
int i = 0;
public ParseDate(int i) {
this.i = i;
}
@Override
public void run() {
if (tl.get() == null) {
tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
try {
Date t = tl.get().parse("2016-06-29" + i % 60);
System.out.println(i + ":" + t);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
使用ThreadLocal为每一个线程都产生一个SimpleDateFormt对象实例。
实现原理(Jdk1.8的源码):其内部数据结构可以理解为一个HashMap,用户存储线程引用
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);
}
get
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
/**
* Remove the entry for key.
*/
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
如果对象对于竞争的处理容易引起性能损失,应考虑使用ThreadLocal为每个线程分配单独的对象。典型案例就是多线程环境下产生随机数。