ThreadLocal的官方API解释为:java
“该类提供了线程局部 (thread-local) 变量。这些变量不一样于它们的普通对应物,由于访问某个变量(经过其 get 或 set 方法)的每一个线程都有本身的局部变量,它独立于变量的初始化副本。ThreadLocal 实例一般是类中的 private static 字段,它们但愿将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。”编程
大概的意思有两点:数组
ThreadLocal一般用来共享数据,当你想在多个方法中使用某个变量,这个变量是当前线程的状态,其它线程不依赖这个变量,你第一时间想到的就是把变量定义在方法内部,而后再方法之间传递参数来使用,这个方法能解决问题,可是有个烦人的地方就是,每一个方法都须要声明形参,多处声明,多处调用。影响代码的美观和维护。有没有一种方法能将变量像private static形式来访问呢?这样在类的任何一处地方就都能使用。这个时候ThreadLocal大显身手了。安全
import java.util.HashMap;多线程
import java.util.Map;并发
public class TreadLocalTest {ide
// static ThreadLocal<HashMap> threadLocal = new ThreadLocal<HashMap>(){源码分析
// @Override性能
// protected HashMap initialValue() {this
// System.out.println(Thread.currentThread().getName()+”initialValue”);
// return new HashMap();
// }
// };
public static class T1 implements Runnable {
private final static Map map = new HashMap();
int id;
public T1(int id) {
this.id = id;
}
public void run() {
// Map map = threadLocal.get();
for (int i = 0; i < 20; i++) {
map.put(i, i + id * 100);
try {
Thread.sleep(100);
} catch (Exception ex) {
}
}
System.out.println(Thread.currentThread().getName()
+ “# map.size()=” + map.size() + ” # ” + map);
}
}
public static void main(String[] args) {
Thread[] runs = new Thread[15];
T1 t = new T1(1);
for (int i = 0; i < runs.length; i++) {
runs[i] = new Thread(t);
}
for (int i = 0; i < runs.length; i++) {
runs[i].start();
}
}
}
这段程序的本意是,启动15个线程,线程向map中写入20个整型值,而后输出map。运行该程序,观察结果,咱们会发现,map中压根就不止20个元素,这说明程序产生了线程安全问题。
咱们都知道HashMap是非线程安全的,程序启动了15个线程,他们共享了同一个map,15个线程都往map写对象,这势必引发线程安全问题。
咱们有两种方法解决这个问题:
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();
}
getMap方法的源码:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
该方法返回的是当前线程中的ThreadLocalMap实例。阅读Thread的源码咱们发现Thread中有以下变量声明:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
咱们暂时能够将ThreadLocalMap理解为一个相似Map的这么个类,以后再讲解它。
get()方法的大体意思就是从当前线程中拿到ThreadLocalMap的实例threadLocals,若是threadLocals不为空,那么就以当前ThreadLocal实例为KEY从threadLocals中拿到对应的VALUE。若是不为空,那么就调用setInitialValue()方法初始化threadLocals,最终返回的是initialValue()方法的返回值。下面是setInitialValue()方法的源码
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;
}
咱们看到map.set(this, value);这句代码将ThreadLocalMap的实例做为KEY,将initialValue()的返回值做为VALUE,set到了threadLocals中。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
咱们看到它比setInitialValue()方法就少了个return语句。这两种方式都能达到初始化ThreadLocalMap实例的效果。
ThreadLocal类只有三个属性,以下:
/*ThreadLocal的hash值,map用它来存储值*/
private final int threadLocalHashCode = nextHashCode();
/*改类能以原子的方式更新int值,这里主要是在产生新的ThreadLocal实例时用来产生一个新的hash值,map用该值来存储对象*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/*该变量标识每次产生新的ThreadLocal实例时,hash值的增量*/
private static final int HASH_INCREMENT = 0x61c88647;
剩下的就是一些方法。最关键的地方就是ThreadLocal定义了一个静态内部类ThreadLocalMap。咱们在下一章节再来分析这个类。从ThreadLocal的类结构,咱们能够看到,实际上问题的关键先生是ThreadLocalMap,ThreadLocal只是提供了管理的功能,咱们也能够说ThreadLocal只是代理了ThreadLocalMap而已。
private void set(ThreadLocal key, Object value) {
// We don’t use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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)]) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
这个方法的主要功能就是讲KEY-VALUE存储到ThreadLocalMap中,这里至少咱们看到KEY其实是key.threadLocalHashCode,ThreadLocalMap一样维护着Entry数组,这个Entry咱们在下一节会讲解。这里涉及到了Hash冲突的处理,这里并不会向HashMap同样冲突了以链表的形式日后添加。若是对这个Hash冲突解决方案有兴趣,能够再进一步研究源码。
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
这里咱们看到Entry集成了WeakReference类,泛型声明了ThreadLocal,即每个Entry对象都保留了对ThreadLocal实例的弱引用,之因此这么干的缘由是,线程在结束以后须要将ThreadLocal实例从map中remove调,以便回收内存空间。
首先,ThreadLocalMap并非为了解决线程安全问题,而是提供了一种将实例绑定到当前线程的机制,相似于隔离的效果,实际上本身在方法中new出来变量也能达到相似的效果。ThreadLocalMap跟线程安全基本不搭边,绑定上去的实例也不是多线程公用的,而是每一个线程new一份,这个实例确定不是共用的,若是共用了,那就会引起线程安全问题。ThreadLocalMap最大的用处就是用来把实例变量共享成全局变量,在程序的任何方法中均可以访问到该实例变量而已。网上不少人说ThreadLocalMap是解决了线程安全问题,实际上是望文生义,二者不是同类问题。
转载自并发编程网 – ifeve.com本文连接地址: ThreadLocal使用