Java并发编程的艺术笔记(四)——ThreadLocal的使用

Wesley13
• 阅读 731

ThreadLocal,即线程变量,是一个以****ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。目的就是为了让线程能够有自己的变量

可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。

/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {

        //获取当前线程
        Thread t = Thread.currentThread();
        //得到线程的ThredLocalMap
        ThreadLocalMap map = getMap(t);
        //如果map不为空,则将当前线程的对象作为key,传进来的参数作为value存储
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

看一下ThredLocalMap是什么:

static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        .......

看到这是ThreadLocal的一个内部类,使用Entry类进行存储。K是我们的ThredLocal对象。

总结:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象

再来看下get方法:

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

拿到这个entry的value。

ThreadLocal本身并不存值,它只是作为ThreadLocalMap的key,来获取value,因此能实现数据隔离。

注意:由于ThreadLocalMap的生命周期和Thread一样长,因此要手动remove掉对应的key,不然会造成内存泄露。

使用场景:

1.管理Connection,尤其是管理数据库连接。

频繁创建和关闭connection是一件很耗时的操作,因此要用到数据库连接池。ThreadLocal可以很好的管理数据库连接,因为它能够实现当前线程的操作都是用同一个Connection,保证了事务!

public class ConnectionUtil {
    private static Logger logger = LoggerFactory.getLogger(ConnectionUtil.class);
    //数据库连接池
    private static BasicDataSource dataSource;
    //为不同的线程管理连接
    private static ThreadLocal<Connection> local;
    
    static {
        BufferedReader br = null;
        Properties ipp_prop = new Properties();
        
        try {
            String propertiesurl = System.getProperty("user.dir") + "/ipp_parser.properties";
            br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(propertiesurl)), "utf-8"));
            ipp_prop.load(br);
            br.close();
        }  catch (Exception e1) {
            e1.printStackTrace();
        }
        
        dataSource = new BasicDataSource();
        dataSource.setDriverClassName(ipp_prop.getProperty("db.driver"));
        dataSource.setUrl(ipp_prop.getProperty("db.url"));
        dataSource.setUsername(ipp_prop.getProperty("db.user"));
        dataSource.setPassword(ipp_prop.getProperty("db.password"));
        //初始连接
        dataSource.setInitialSize(Integer.parseInt(ipp_prop.getProperty("db.initsize")));
        //最大连接
        dataSource.setMaxTotal(Integer.parseInt(ipp_prop.getProperty("db.maxtotal")));
        //最长等待时间
        dataSource.setMaxWaitMillis(Integer.parseInt(ipp_prop.getProperty("db.maxwait")));
        //最小空闲
        dataSource.setMinIdle(Integer.parseInt(ipp_prop.getProperty("db.minidle")));
        dataSource.setMaxIdle(Integer.parseInt(ipp_prop.getProperty("db.maxidle")));
        //初始化线程池本地
        local = new ThreadLocal<>();/**得到连接
     * @return
     * @throws SQLException
     */
    public static Connection getOracleConnection() throws SQLException {
        //获取Connection对象
        Connection connection = dataSource.getConnection();
        //把Connection放进local里
        local.set(connection);
        logger.info("get oracleConnection");
        return connection;
    }
    
    public static void closeOracleConnection(){
        Connection connection = local.get();
        
        try {
            if (connection != null) {
                //设置自动提交
                connection.setAutoCommit(true);
                //连接还给连接池
                connection.close();
                local.remove();
                logger.info("close oracleConnection");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
点赞
收藏
评论区
推荐文章
待兔 待兔
3年前
ThreadLocal源码分析
最近在学多线程并发的知识,发现好像ThreadLoca还挺重要,决定看看源码以及查找各方资料来学习一下。ThreadLocal能够提供线程的局部变量,让每个线程都可以通过set/get来对这个局部变量进行操作,不会和其它线程的局部变量进行冲突,实现了线程的数据隔离。首先是ThreadLocal的结构:每个Thread维护一个ThreadLocalMap,这个
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java ThreadLocal
ThreadLocal是什么定义:提供线程局部变量;一个线程局部变量在多个线程中,分别有独立的值(副本)特点:简单(开箱即用)、快速(无额外开销)、安全(线程安全)场景:多线程场景(资源持有、线程一致性、并发计算、线程安全等场景)ThreadLocal基本API 构
ThreadLocal源码解析及实战应用
ThreadLocal是一个关于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。ThreadLocal在设计之初就是为解决并发问题而提供一种方案,每个线程维护一份自己的数据,达到线程隔离的效果。
Wesley13 Wesley13
3年前
Java ThreadLocal的内存泄漏问题
ThreadLocal提供了线程独有的局部变量,可以在整个线程存活的过程中随时取用,极大地方便了一些逻辑的实现。常见的ThreadLocal用法有:\存储单个线程上下文信息。比如存储id等;\使变量线程安全。变量既然成为了每个线程内部的局部变量,自然就不会存在并发问题了;\减少参数传递。比如做一个trace工具,能够输出工程从开始到结
Wesley13 Wesley13
3年前
Java多线程与并发之ThreadLocal原理解析
1\.ThreadLocal是什么?使用场景ThreadLocal简介ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
Wesley13 Wesley13
3年前
Java多线程与并发之ThreadLocal
1\.ThreadLocal是什么?使用场景ThreadLocal简介ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
Wesley13 Wesley13
3年前
JAVA基础系列:ThreadLocal
1. 思路1.什么是ThreadLocal?ThreadLocal类顾名思义可以理解为线程本地变量。也就是说如果定义了一个ThreadLocal,每个线程往这个ThreadLocal中读写是线程隔离,互相之间不会影响的。它提供了一种将可变数据通过每个线程有自己的独立副本从而实现线程封闭的机制。2.它大致的实现
Easter79 Easter79
3年前
ThreadLocal 详解
概念ThreadLocal用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。也就是说ThreadLocal可以为每个线程创建一个【单独的变量副本】,相当于线程的privatestatic类型变量。使用示例publicclassThreadLocalTest{
京东云开发者 京东云开发者
9个月前
一次因PageHelper引起的多线程复用问题的排查和解决 | 京东物流技术团队
A、ProblemDescription1\.PageHelper方法使用了静态的ThreadLocal参数,在startPage()调用紧跟MyBatis查询方法后,才会自动清除ThreadLocal存储的对象。2\.当一个线程先执行了A方法的PageH