API中ThreadLocal的方法:
public T get() 返回此线程局部变量的当前线程副本中的值。
protected T initialValue() 返回此线程局部变量的当前线程的初始值。
public void remove() 移除此线程局部变量当前线程的值。
public void set(T value) 将此线程局部变量的当前线程副本中的值设置为指定值。
1.看源码得到Thread 。ThreadLocal。ThreadLocalMap之间的关系。
(1)ThreadLocalMap是ThreadLocal的内部类
1 static class ThreadLocalMap { 2 3 /** 4 * The entries in this hash map extend WeakReference, using 5 * its main ref field as the key (which is always a 6 * ThreadLocal object). Note that null keys (i.e. entry.get() 7 * == null) mean that the key is no longer referenced, so the 8 * entry can be expunged from table. Such entries are referred to 9 * as "stale entries" in the code that follows. 10 */ 11 static class Entry extends WeakReference<ThreadLocal> { 12 /** The value associated with this ThreadLocal. */ 13 Object value; 14 15 Entry(ThreadLocal k, Object v) { 16 super(k); 17 value = v; 18 } 19 }
(2) Thread持有两个ThreadLocalMap的引用
1 /* ThreadLocal values pertaining to this thread. This map is maintained 2 * by the ThreadLocal class. */ 3 ThreadLocal.ThreadLocalMap threadLocals = null; 4 5 /* 6 * InheritableThreadLocal values pertaining to this thread. This map is 7 * maintained by the InheritableThreadLocal class. 8 */ 9 ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
所以总结出一点:每个线程中都有一个自己的ThreadLocalMap类对象用来保存自身的值。然后ThreadLocal实例调用get()方法时会根据当前线程来取得当前线程的值
(由下面的get()方法分析得出的结论)。
2.从ThreadLocal的set()方法开始分析
1 public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 if (map != null) 5 map.set(this, value); //以ThreadLocal为键 6 else 7 createMap(t, value); 8 } 9 void createMap(Thread t, T firstValue) { 10 t.threadLocals = new ThreadLocalMap(this, firstValue); //以ThreadLocal为键
11 }
3.跟进ThreadLocal的get()方法
1 public T get() { 2 Thread t = Thread.currentThread(); //得到当前线程 3 ThreadLocalMap map = getMap(t); //得到当前线程相关的ThreadLocalMap 4 if (map != null) { 5 ThreadLocalMap.Entry e = map.getEntry(this); 6 if (e != null) 7 return (T)e.value; 8 } 9 return setInitialValue(); 10 } 11 12 ThreadLocalMap getMap(Thread t) { 13 return t.threadLocals; 14 }
3.我觉得理解上面的代码就可以了。下面看看ThreadLocal在Hibernate中的应用
1 private static final ThreadLocal threadSession = new ThreadLocal(); 2 3 public static Session getSession() throws InfrastructureException { 4 Session s = (Session) threadSession.get(); 5 try { 6 if (s == null) { 7 s = getSessionFactory().openSession(); 8 threadSession.set(s); 9 } 10 } catch (HibernateException ex) { 11 throw new InfrastructureException(ex); 12 } 13 return s; 14 }
4.来个总结
我们在多线程的开发中,经常会考虑到的策略是对一些需要公开访问的属性通过设置同步的方式来访问。这样每次能保证只有一个线程访问它,不会有冲突。
但是这样做的结果会使得性能和对高并发的支持不够。在某些情况下,如果我们不一定非要对一个变量共享不可,而是给每个线程一个这样的资源副本,让他们可
以独立都各自跑各自的,这样不是可以大幅度的提高并行度和性能了吗?
还有的情况是有的数据本身不是线程安全的,或者说它只能被一个线程使用,不能被其他线程同时使用。如果等一个线程使用完了再给另外一个线程使用就
根本不现实。这样的情况下,我们也可以考虑用ThreadLocal。一个典型的情况就是我们连接数据库的时候通常会用到连接池。而对数据库的连接不能有多个线程
共享访问。这个时候就需要使用ThreadLocal了。
1 private static ThreadLocal<Connection> connectionHolder = 2 new ThreadLocal<Connection>() { 3 public Connection initialValue() { 4 return DriverManager.getConnection(DB_URL); 5 } 6 }; 7 8 9 pubic static Connection getConnection() { 10 return connectionHolder.get(); 11 }