ThreadLocal

技术文章 1年前 (2020) 完美者
624 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

版权声明:完美者 发表于 2020-09-17 16:28:42。
转载请注明:ThreadLocal | 完美导航

暂无评论

暂无评论...