ThreadLocal的應用

ThreadLocal 內存泄露問題

ThreadLocal自己不存儲值,他只做爲一個key。真正存值的是threadjava

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中刪除數據。sql

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調用就比較麻煩this

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能夠幫咱們實現。url

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();code

相關文章
相關標籤/搜索