码迷,mamicode.com
首页 > 其他好文 > 详细

ThreadLocal

时间:2020-09-17 16:28:42      阅读:25      评论:0      收藏:0      [点我收藏+]

标签:==   hold   会话   weakref   hbase   频繁   sso   关闭   访问   

1、基本概念

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

2、使用场景

1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束;

2、线程间数据隔离;

3、进行事务操作,用于存储线程事务信息;

4、数据库连接,Session会话管理。

  频繁的创建和关闭Connection是一件非常耗费资源的操作,所以需要创建数据库连接池,而数据库连接池的连接的管理任务就交由ThreadLocal来进行管理。为什么交给它来管理呢??ThreadLocal能够实现当前线程的操作都是用同一个Connection,保证了事务!

//保证一个线程当中只有一个连接对象,且修饰符为private,即只能在本类中访问
private ThreadLocal<Connection> connHolder = new ThreadLocal<Connection>();
/**
 * 获取连接对象,建立与HBASE的连接
 */
protected synchronized Connection getConnection() throws IOException {
    //当我们在调用get()方法的时候,先获取当前线程,然后获取到当前线程的ThreadLocalMap对象,
    //如果非空,那么取出ThreadLocal的value,否则进行初始化,初始化就是将initialValue的值set到ThreadLocal中。
    
    //从线程中拿到连接对象
    Connection conn  = connHolder.get();
    if ( conn == null ){
        // 1.获取配置文件信息
        Configuration conf = HBaseConfiguration.create();
        // 2.建立连接,获取connection对象
        conn = ConnectionFactory.createConnection(conf);
        connHolder.set(conn);
    }
    return conn;
}
/**
* 关闭连接
* 这里不是真的把连接关了,只是将该连接归还给连接池
* @throws IOException
*/
 protected void end() throws IOException {
?
        Admin admin = getAdmin();
        if (admin != null){
            admin.close();
            adminHolder.remove();
        }
        Connection conn = getConnection();
        if (conn != null){
            conn.close();
            //既然连接已经归还给连接池了,ThreadLocal保存的Connction对象也已经没用了
            connHolder.remove();
        }
 }

3、方法

(1)set方法

设置ThreadLocal的值,

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

  先获取当前线程对象,然后调用getMap获取ThreadLocalMap,如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去。如果该Map不存在,则初始化一个。分析一下ThreadLocalMap:

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
?
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    ……
   }

  ThreadLocalMap本身是ThreadLocal的一个静态内部类,在它的内部又定义了一个静态内部类Entry用于存储数据,存储数据的方式是key-value的形式,其中ThreadLocal为key,我们存储ThreadLocal中的线程变量为value。getMap方法是根据传入的当前的线程对象返回当前线程对象的成员变量threadLocals。

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

  总结一下,ThreadLocal并不是直接存储我们设置的值,而是通过一个ThreadLocalMap来存储在一个ThreadLocal中的值。其中,一个线程对象对应一个ThreadLocalMap(Thread类内部维护着一个ThreadLocalMap的引用)。ThreadLocalMap本质是一个Entry类,其中以当前的ThreadLocal对象作为key,我们设置的值作为value进行存储,因此ThreadLocal起到的作用是key。

(2)get方法

  返回此线程局部变量的当前线程副本中的值。也就是根据当前的ThreadLocal对象获取存储在ThreadLocal中的value值。

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

  首先获取当前线程,然后调用getMap方法获取一个ThreadLocalMap,如果map不为null,那就使用当前线程作为ThreadLocalMap的Entry的键,然后值就作为相应的的值,如果没有那就通过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;
}

  initialValue方法返回的值是null,初始化的时候,赋予当前线程对象的对应于的ThreadLocalMap中的value值是null。

(3)remove方法

删除此线程局部变量的当前线程值。

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

类似于map中的remove操作。

4、总结

(1)每个Thread维护着一个ThreadLocalMap的引用

ThreadLocal.ThreadLocalMap threadLocals = null;

(2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储

(3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。

(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中

(5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。

(6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。

5、ThreadLocal内存泄漏的问题

通过之前的源码,我们知道,Thread中有一个维护一个map即ThreadLocalMap,而ThreadLocalMap的key是ThreadLocal对象,值是我们通过ThreadLocal的set方法设置的;ThreadLocal是继承了WeakReference是一个弱引用,当为null时,会被当成垃圾回收。

但是,重点来了,如果我们ThreadLocal是null了,此时要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。

解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。

参考:

https://baijiahao.baidu.com/s?id=1653790035315010634&wfr=spider&for=pc

ThreadLocal

标签:==   hold   会话   weakref   hbase   频繁   sso   关闭   访问   

原文地址:https://www.cnblogs.com/yxym2016/p/13622059.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!