面试题答案
一键面试1. ThreadLocal工作原理
- 数据结构:
ThreadLocal
内部维护了一个ThreadLocalMap
,ThreadLocalMap
是ThreadLocal
的静态内部类。ThreadLocalMap
使用线性探测法解决哈希冲突,它的键为ThreadLocal
对象的弱引用,值为线程对应的本地变量。- 每个
Thread
对象内部都有一个threadLocals
属性,类型为ThreadLocal.ThreadLocalMap
,当线程第一次调用ThreadLocal
的set
方法时,会在该线程的threadLocals
中创建一个新的ThreadLocalMap
实例。
- 线程隔离实现:
- 当调用
ThreadLocal
的get
方法时,它首先获取当前线程,然后从当前线程的threadLocals
中根据ThreadLocal
自身作为键来获取对应的值,这样每个线程都有自己独立的变量副本,实现了线程隔离。 - 同理,
set
方法也是将值存放在当前线程的threadLocals
中,与其他线程的数据相互隔离。
- 当调用
2. 实际项目应用场景
- 数据库连接管理:在多线程Web应用中,每个线程可能需要独立的数据库连接。使用
ThreadLocal
可以为每个线程创建并管理自己的数据库连接,避免多线程竞争同一连接导致的问题。例如,在Spring的事务管理中,如果使用ThreadLocal
来管理数据库连接,在同一个线程的事务内,所有数据库操作都使用同一个连接,保证事务的一致性。public class DatabaseConnectionUtil { private static final ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>(); public static Connection getConnection() { Connection connection = connectionThreadLocal.get(); if (connection == null) { // 创建数据库连接逻辑 connection = DriverManager.getConnection(url, username, password); connectionThreadLocal.set(connection); } return connection; } public static void closeConnection() { Connection connection = connectionThreadLocal.get(); if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } connectionThreadLocal.remove(); } } }
- 用户会话管理:在Web应用中,每个线程处理一个用户请求,可能需要在整个请求处理过程中保持用户会话信息。
ThreadLocal
可以用来存储当前线程处理请求时的用户会话数据,如用户登录信息等,方便在不同的业务逻辑层之间传递,而不需要通过方法参数层层传递。
3. 使用陷阱及避免方法
- 内存泄漏问题:
- 陷阱:
ThreadLocalMap
的键是ThreadLocal
的弱引用。如果ThreadLocal
对象没有其他强引用指向它,在垃圾回收时,ThreadLocal
对象会被回收,但ThreadLocalMap
中对应的键值对依然存在,值对象不会被回收,导致内存泄漏。 - 避免方法:在使用完
ThreadLocal
后,及时调用remove
方法清除ThreadLocalMap
中的对应键值对。例如,在上述数据库连接管理示例中,在closeConnection
方法中调用了connectionThreadLocal.remove()
。
- 陷阱:
- 父子线程传递问题:
- 陷阱:
ThreadLocal
不能自动将数据传递给子线程。如果在父线程中设置了ThreadLocal
的值,子线程默认获取不到。 - 避免方法:可以使用
InheritableThreadLocal
类,它是ThreadLocal
的子类,能够实现父子线程间的数据传递。例如,在Web应用中,如果父线程处理请求时设置了用户会话信息到InheritableThreadLocal
,子线程在处理异步任务时可以获取到相同的用户会话信息。
- 陷阱: