提供了保持对象的方法和避免参数传递的对象访问方式
常用方法
get
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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(); }
|
getMap方法返回当前线程threadLocals属性
1 2 3
| ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
|
threadLocals为ThreadLocalMap类型,被定义在Thread类当中,默认为null
1 2 3 4 5
| public class Thread implements Runnable { ThreadLocal.ThreadLocalMap threadLocals = null; }
|
ThreadLocalMap为ThreadLocal中定义的静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ThreadLocal<T> { static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } } } }
|
map为空的时候,调用setInitialValue方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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; } protected T initialValue() { return null; }
|
创建Map的方法
1 2 3
| void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
|
set
1 2 3 4 5 6 7 8
| public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
|
存取原理
每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,其键值为当前ThreadLocal变量,value为要存储的数据
初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,
并且以当前ThreadLocal变量为键值,要保存的数据为value,存到threadLocals
然后在当前线程里面,就可以通过get方法在threadLocals里面查找
典型应用
hibernate中典型的ThreadLocal的应用:Session管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private static final ThreadLocal threadSession = new ThreadLocal(); public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s; }
|
注意事项
ThreadLocal不是用来解决共享对象的多线程访问问题的,
通常,通过ThreadLocal.set()到线程中的对象是该线程自己使用的对象,其他线程无法访问
ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,
而是通过每个线程中的new对象 的操作来创建的对象,每个线程创建一个,不是对象的拷贝或副本,
通过ThreadLocal.set()将这个新创建的对象的引用保存到各线程的自己的一个map中,
每个线程都有这样一个map,执行ThreadLocal.get()时,各线程从自己的map中取出放进去的对象,
因此取出来的是各自自己线程中的对象,ThreadLocal实例是作为map的key来使用的
总结
每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,
然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递。