ThreadLocal 用于在多线程环境中维护线程封闭(thread-local)的变量。线程封闭是一种确保变量在多线程环境中的线程安全性的机制,每个线程都有自己独立的变量副本,互不干扰。ThreadLocal 提供了一种简单的方式来实现线程封闭,它为每个线程都创建了一个独立的变量副本。
使用场景
注意事项
主要方法说明
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("Some Value");
set
方法设置过值,那么 get
将返回 null
。ThreadLocal<String> threadLocal = new ThreadLocal<>();
String value = threadLocal.get();
void remove() 移除当前线程的线程局部变量。
移除后,如果再次调用 get
方法,将返回 null
。
通常在线程结束时或者线程池中线程重用之前调用,以避免内存泄漏。
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.remove();
使用 ThreadLocal threadLocal = new ThreadLocal<>() 声明一个 threadLocal 对象,然后在任意线程中都可以使用 threadLocal 对象。通过测试发现,每线程中通过 threadLocal.set 保存的值都只能在当前线程中使用。
public class ThreadLocalExample {
// 创建一个 ThreadLocal 变量
private static ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
// 在主线程中设置 ThreadLocal 变量的值
threadLocal.set("主线程 ThreadLocal");
// 创建并启动两个线程
Thread thread1 = new Thread(() -> {
// 在线程1中设置 ThreadLocal 变量的值
threadLocal.set("线程 1 ThreadLocal");
printThreadLocalValue(); // 输出线程1的值
});
Thread thread2 = new Thread(() -> {
// 在线程2中设置 ThreadLocal 变量的值
threadLocal.set("线程 2 ThreadLocal");
printThreadLocalValue(); // 输出线程2的值
});
thread1.start();
thread2.start();
// 在主线程中获取 ThreadLocal 变量的值
printThreadLocalValue(); // 输出主线程的值
}
private static void printThreadLocalValue() {
// 获取当前线程的 ThreadLocal 变量值
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
}
}
如下创建一个线程安全的数据库连接管理类 DatabaseConnectionManager,然后在其他线程中就可以安全的使用数据库连接了。
public static class DatabaseConnectionManager {
// 使用 ThreadLocal 来存储数据库连接
private static ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> {
try {
// 创建数据库连接,实例应用中,这里可以从连接池中获取。
return DriverManager.getConnection("jdbc:mysql://192.168.8.70:3306/mysql", "root", "123456");
} catch (SQLException e) {
throw new RuntimeException("不能创建数据连接", e);
}
});
// 获取当前线程的数据库连接
public static Connection getConnection() {
return connectionHolder.get();
}
// 关闭当前线程的数据库连接
public static void closeConnection() {
try {
Connection connection = connectionHolder.get();
if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
// 处理异常
e.printStackTrace();
} finally {
// 清除 ThreadLocal 中的值,避免内存泄漏
connectionHolder.remove();
}
}
}
完整测试示例
package top.yiqifu.study.p021_limit;
import java.sql.*;
// 包装类
public class Test113_ThreadLocalDB {
public static void main(String[] args) {
// 模拟多个线程使用数据库连接
for (int i = 1; i <= 5; i++) {
Thread thread = new Thread(new DatabaseTask("线程" + i));
thread.start();
}
}
// 模拟数据库操作的任务
private static class DatabaseTask implements Runnable {
private final String threadName;
public DatabaseTask(String threadName) {
this.threadName = threadName;
}
@Override
public void run() {
try {
// 获取数据库连接
Connection connection = DatabaseConnectionManager.getConnection();
System.out.println(threadName + ": 获取数据库连接");
// 模拟数据库操作
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from sys.sys_config");
if(resultSet.next()){
System.out.println(threadName + ": 获取数据库数据"+ resultSet.getString(1));
}
resultSet.close();
statement.close();
// 关闭数据库连接
DatabaseConnectionManager.closeConnection();
System.out.println(threadName + ": 关闭数据库连接");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static class DatabaseConnectionManager {
// 使用 ThreadLocal 来存储数据库连接
private static ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> {
try {
// 创建数据库连接
return DriverManager.getConnection("jdbc:mysql://192.168.8.70:3306/mysql", "root", "yiqifu");
} catch (SQLException e) {
throw new RuntimeException("不能创建数据连接", e);
}
});
// 获取当前线程的数据库连接
public static Connection getConnection() {
return connectionHolder.get();
}
// 关闭当前线程的数据库连接
public static void closeConnection() {
try {
Connection connection = connectionHolder.get();
if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
// 处理异常
e.printStackTrace();
} finally {
// 清除 ThreadLocal 中的值,避免内存泄漏
connectionHolder.remove();
}
}
}
}
注意如果是 MySQL 5.7,则需要在 pom.xml 添加
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency>
在 Spring MVC 源码(DispatcherServlet)中使用 ThreadLocal 的示例
public class DispatcherServlet extends FrameworkServlet {
private static final ThreadLocal<HttpServletRequest> requestThreadLocal = new ThreadLocal<>();
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
try {
// 将当前请求对象存储到 ThreadLocal 中
requestThreadLocal.set(request);
// 执行具体的请求处理逻辑
super.doService(request, response);
} finally {
// 清除 ThreadLocal 中的值,避免内存泄漏
requestThreadLocal.remove();
}
}
// 在其他地方可以通过此方法获取当前线程的 HttpServletRequest
public static HttpServletRequest getCurrentRequest() {
return requestThreadLocal.get();
}
}