1.ThreadLocal线程隔离和内存泄漏
1.get方法:是用来获取ThreadLocal在线程中保存的变量副本
2.set方法:是用来设置当前线程中的变量副本
3.remove方法:是用来移除当前线程的变量副本
4.initialValue方法:被protected修饰,一般在使用的时候可以重写,初始化变量副本
5.ThreadLocalMap:ThreadLocal的静态内部类,里面维护了一个Entry数组,主要用来保存线程的本地变量副本
6.Entry:Entry是ThreadLocalMap的静态内部类,继承了弱引用 WeakReference<ThreadLocal<?>> ,弱引用可以帮助程序更好的GC,
更好的做垃圾回收,而ThreadLocal<?>是作为Entry的key,Entry的key被GC掉,会导致K为NULL的Entry无法被移除,这就造成了内存泄漏。
7.内存泄漏的避免:在使用完ThreadLocal中的本地线程变量副本后清除掉副本。
8.ThreadLocal和synchronized关键字完全相反,ThreadLocal是空间换时间,synchronized是时间换空间,
ThreadLocal用于线程隔离,实现每个线程使用自己的本地变量副本,以解决并发问题, synchronized用于多线程间共享数据
9.内存溢出(memory overflow):是指不能申请到足够的内存进行使用,就会发生内存溢出,比如出现的OOM(Out Of Memory)
10.内存泄漏(memory lack):内存泄露是指在程序中已经动态分配的堆内存由于某种原因未释放或者无法释放(已经没有用处了,但是没有释放),造成系统内存的浪费,这种现象叫“内存泄露”。
当内存泄露到达一定规模后,造成系统能申请的内存较少,甚至无法申请内存,最终导致内存溢出,所以内存泄露是导致内存溢出的一个原因。
2.ThreadLocal的源码
public class ThreadLocal<T> {
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();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
}
static class ThreadLocalMap {
private static final int INITIAL_CAPACITY = 16;
private int threshold;
private Entry[] table;
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
3.ThreadLocal使用示例
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.pool.HikariPool;
import java.sql.Connection;
import java.sql.SQLException;
public class JdbcUtils {
private static final ThreadLocal<Connection> localConnection = new ThreadLocal<>();
private static final HikariPool hikariPool;
static
{
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/day1106");
config.setDriverClassName("com.mysql.jdbc.Driver");
config.setUsername("root");
config.setPassword("root");
config.setMinimumIdle(1);
config.setMaximumPoolSize(10);
hikariPool = new HikariPool(config);
}
public static Connection getInstance() throws SQLException {
Connection connection = localConnection.get();
if (connection!=null)
{
return connection;
}
else
{
Connection cc = hikariPool.getConnection();
localConnection.set(cc);
return cc;
}
}
public static void removeLocalConnection(){
localConnection.remove();
}
}
import lombok.extern.slf4j.Slf4j;
import java.sql.*;
@Slf4j
public class CRUD {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
try
{
@SuppressWarnings("SqlResolve")
String sql = "SELECT * FROM role WHERE role_id=?";
connection = JdbcUtils.getInstance();
connection.setAutoCommit(false);
statement = connection.prepareStatement(sql);
statement.setInt(1,1);
rs = statement.executeQuery();
while (rs.next()){
int role_id = rs.getInt("role_id");
System.out.println("role_id = " + role_id);
String role_name = rs.getString("role_name");
System.out.println("role_name = " + role_name);
Date create_time = rs.getDate("create_time");
System.out.println("create_time = " + create_time);
Date update_time = rs.getDate("update_time");
System.out.println("update_time = " + update_time);
}
connection.commit();
}
catch (SQLException e)
{
log.error(e.getLocalizedMessage(),e);
try
{
if (connection!=null)
{
connection.rollback();
}
}
catch (SQLException ex)
{
log.error(ex.getLocalizedMessage(),ex);
}
}
finally
{
JdbcUtils.removeLocalConnection();
if (connection!=null)
{
try
{
connection.close();
}
catch (SQLException e)
{
log.error(e.getLocalizedMessage(),e);
}
}
if (statement!=null)
{
try
{
statement.close();
}
catch (SQLException e)
{
log.error(e.getLocalizedMessage(),e);
}
}
if (rs!=null)
{
try
{
rs.close();
}
catch (SQLException e)
{
log.error(e.getLocalizedMessage(),e);
}
}
}
}
}
4.ThreadLocal内存分布图
5.ThreadLocal使用拓展:阅读spring-web
- RequestContextFilter
- RequestContextHolder
- RequestContextListener