ThreadLocal

https://www.wailian.work/images/2018/10/16/ThreadLocal.pngThreadLocal

  • ThreadLocal类中维护ThreadLocalMap,用于存储每个线程的变量副本。ThreadLocalMapEntry的键为线程对象,而值为对应线程的变量副本,Entry(ThreadLocal k, Object v)。采用空间换时间。
  • ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。归纳了两点:
    1. 每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。
    2. 将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。

应用场景

  • 最常见的ThreadLocal使用场景为:解决数据库连接、Session管理等。
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
	public Connection initialValue() {
		return DriverManager.getConnection(DB_URL);
	}
};

public static Connection getConnection() {
	return connectionHolder.get();
}
  • Hibernate中典型的ThreadLocal的应用:
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内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key的value就会导致内存泄漏,而不是因为弱引用。
  • 避免内存泄漏:每次使用完ThreadLocal,都调用它的remove()方法,清除数据。

示例

  • ThreadLocalTest, ThreadLocalNoSetTest
// java.lang.Thread
/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;