ThreadLocal 内存泄露问题
ThreadLocal本身不存储值,他只作为一个key。真正存值的是thread
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();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//t.threadLocals是ThreadLocal.ThreadLocalMap类型
}
也就是说,threadlocal的生命周期是一个普通的变量的生命周期,而threadLocal中存放的数据的生命周期是当前thread的生命周期(thread不销毁一直都存在)。如果遇到thread不销毁的情况,比如使用线程池,不断创建新的threadLocal作为key 向 Thread.threadLocals中插入对象,而之前的对象也不会被回收,因为有Thread.threadLocals的引用。这样会造成内存泄露。所以需要在使用完成后,手动从threadLocal中删除数据。
ThreadLocal 应用
通常我们写一个工具类都是无状态的。例如下面写一个db的工具类。
public class DatabaseHelper1 {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseHelper.class);
private static final String DRIVER;
private static final String URL;
private static final String USERNAME;
private static final String PASSWORD;
static {
Properties conf = PropsUtil.loadProps("config.properties");
String driver = conf.getProperty("jdbc.driver");
String url = conf.getProperty("jdbc.url");
String username = conf.getProperty("jdbc.username");
String password = conf.getProperty("jdbc.password");
DRIVER = driver;
URL = url;
USERNAME = username;
PASSWORD = password;
}
//示例代码
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USERNAME, PASSWORD);
}
//示例代码
public static <T>List<T> queryEntityList(Connection connection, Class<T> entityClass, String sql, Object... params) {
return new ArrayList<>();
}
}
这样的话service调用就比较麻烦
public List<Customer> getCustomerList1() throws SQLException {
Connection connection = DatabaseHelper1.getConnection();
String sql = "select * from customer";
return DatabaseHelper1.queryEntityList(connection,Customer.class, sql);
}
能不能让DatabaseHelper工具类有记忆功能?不用每次service都是先取到connection再做其他操作。threadLocal可以帮我们实现。
public class DatabaseHelper {
private static ThreadLocal<Connection> threadConnection = new ThreadLocal<>();
public static Connection getConnection() {
Connection connection = threadConnection.get();
if (connection == null) {
try {
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (SQLException e) {
LOGGER.error("get connection fail");
} finally {
threadConnection.set(connection);
}
}
return connection;
}
public static void closeConnetion(Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
LOGGER.error("close connection fail");
} finally {
threadConnection.remove();
}
}
}
public static <T> List<T> queryEntityList(Class<T> entityClass, String sql, Object... params) {
List<T> entityList = new ArrayList<>();
Connection connection = getConnection();
try {
entityList = QUERY_RUNNER.query(connection, sql, new BeanListHandler<T>(entityClass), params);
} catch (SQLException e) {
LOGGER.error("queryEntity fail", e);
} finally {
closeConnetion(connection);
}
return entityList;
}
}
通过threadLocal,使service调用变的简单
public List<Customer> getCustomerList() {
String sql = "select * from customer";
return DatabaseHelper.queryEntityList(Customer.class, sql);
}
但是还记得上面提到的threadlocal内存泄露么,在每次使用完threadLocal记得将其内容删除。就是closeConnection()中的threadConnection.remove();