文档章节

ThreadLocal的应用

4rnold
 4rnold
发布于 2017/09/11 00:25
字数 569
阅读 50
收藏 0

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

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

© 著作权归作者所有

4rnold
粉丝 1
博文 51
码字总数 26866
作品 0
深圳
程序员
私信 提问
加载中

评论(0)

一句话讲清楚什么是JavaEE

Java技术不仅是一门编程语言而且是一个平台。同时Java语言是一门有着特定语法和风格的高级的面向对象的语言,Java平台是Java语言编写的特定应用程序运行的环境。Java平台有很多种,很多的Jav...

qq58edeba279279
2018/06/26
0
0
Java SE是什么?Java各个版本区别

一、Java的各个常用版本: Java SE、java EE、Java ME三个版本。下面介绍各个版本区别: (Java SE下载:https://www.oracle.com/technetwork/java/javase/downloads/index.html) 1. Java ...

osc_iybnxd76
2019/04/25
6
0
Java入门教程一(Java简介)

#什么是Java语言Java 是由 Sun Microsystems 公司于 1995 年推出的一门面向对象程序设计语言。2010 年 Oracle 公司收购 Sun Microsystems,之后由 Oracle 公司负责 Java 的维护和版本升级。 ...

osc_i5nfw8fz
04/16
12
0
一句话讲清楚什么是JavaEE

摘要: Java技术不仅是一门编程语言而且是一个平台。同时Java语言是一门有着特定语法和风格的高级的面向对象的语言,Java平台是Java语言编写的特定应用程序运行的环境。Java平台有很多种,很多...

qq592fbb5b34ad7
2018/07/02
0
0
SUN JDK的网友评论

SUN公司的Java开发工具包,原汁原味的。 JDK 6.0 文档: http://www.oschina.net/uploads/doc/j2se/index.html JDK 6.0 JavaDoc:http://www.oschina.net/home/apidoc?api=javase-6-doc-api......

红薯
2009/12/11
319
1

没有更多内容

加载失败,请刷新页面

加载更多

MySQL Not exists - 一个你不需要会的语法

not exists - 就是字面意思: 不存在. A表里某个字段的值不在B表里; 返回符合条件的A表记录. 如: select a.id, a.codefrom table_a a where not exists ( select b.code from table_...

园领T
34分钟前
19
0
零基础Python学习路线及每个阶段学习目标

零基础Python学习路线及阶段学习目标,首先应该夯实Python核心基础、Web前端编程、Django开发框架、Flask开发框架、爬虫与数据分析等知识,理解机器学习相关的基本概念及系统处理流程。 零基...

每天学Python
37分钟前
18
0
编译flink 源码

首先clone源码 git clone git://github.com/apache/flink.git 然后切换到blink分支 git checkout blink 编辑 flink-filesystems 下的pom文件,注释掉 mapr,如下 <modules><module......

jingshishengxu
41分钟前
28
0
springBoot之配置文件的读取以及过滤器和拦截器的使用

springBoot之配置文件的读取以及过滤器和拦截器的使用 前言 在之前的学习springBoot中,成功的实现了Restful风格的基本服务。但是想将之前的工程作为一个项目来说,那些是仅仅不够的。可能还...

鹏十二
56分钟前
33
0
Springboot在Idea Intellij汉字Unicode读取乱码问题

问题核心:Unicode编码中的字母需要小写。比如\u8bbe\u5907\u53f7 首先,了解清楚Unicode编码是什么东西。请读者自己搜索。 然后,我们要清楚springboot是怎么加载配置文件的。兴趣的读者可以...

sunranhou
今天
21
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部