标签:== hold 会话 weakref hbase 频繁 sso 关闭 访问
ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
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();
}
}
设置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。
返回此线程局部变量的当前线程副本中的值。也就是根据当前的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。
删除此线程局部变量的当前线程值。
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }
类似于map中的remove操作。
(1)每个Thread维护着一个ThreadLocalMap的引用
ThreadLocal.ThreadLocalMap threadLocals = null;
(2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
(3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。
(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中
(6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
通过之前的源码,我们知道,Thread中有一个维护一个map即ThreadLocalMap,而ThreadLocalMap的key是ThreadLocal对象,值是我们通过ThreadLocal的set方法设置的;ThreadLocal是继承了WeakReference是一个弱引用,当为null时,会被当成垃圾回收。
但是,重点来了,如果我们ThreadLocal是null了,此时要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。
解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。
参考:
标签:== hold 会话 weakref hbase 频繁 sso 关闭 访问
原文地址:https://www.cnblogs.com/yxym2016/p/13622059.html