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使用拓展:閱讀spring-web
- RequestContextFilter
- RequestContextHolder
- RequestContextListener