threadlocal存连接对象的目的_浅析​ThreadLocal

ThreadLocal类用来提供线程内部的局部变量。这些变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量,ThreadLocal实例通常来说都是private static类型。 ThreadLocal不是为了解决多线程访问共享变量,而是为每个线程创建一个单独的变量副本,提供了保持对象的方法和避免参数传递的复杂性。

在Spring,Hibernate,Struts2中对于ThreadLocal都有广泛的应用,在Hibernate中获取connection也是通过ThreadLocal来封装connection对象,实现了连接connection之间不会相互影响的效果。在Struts2中通过ThreadLocal来封装每个请求,让每个用户的request请求分离达到不同用户线程之间获取不同的request对象效果。

ThreadLocal原理

ThreadLocal可以看做是一个容器,容器里面存放着属于当前线程的变量。使用一个Map来把线程作为key,对象作为value来存储对象,这样能上达到各个线程能够存储一个对象,让每个线程的对象独立分割开来的效果。

对于ThreadLocal的基本原理,我们可以提供一个基本模拟版本给大家参考。

publicclassSimpleThreadLocal {

privateMap valueMap = Collections.synchronizedMap(newHashMap());

publicvoidset(Object newValue) {

//键为线程对象,值为本线程的变量副本

valueMap.put(Thread.currentThread(), newValue);

}

publicObject get() {

Thread currentThread = Thread.currentThread();

//返回本线程对应的变量

Object o = valueMap.get(currentThread);

//如果在Map中不存在,放到Map中保存起来

if(o == null&& !valueMap.containsKey(currentThread)) {

o = initialValue();

valueMap.put(currentThread, o);

}

returno;

}

publicvoidremove() {

valueMap.remove(Thread.currentThread());

}

publicObject initialValue() {

returnnull;

}

}

SimpleThreadLocal 一共提供了4个方法

void set(Object newValue):根据当前线程,来设置当前线程的对象值,

Object get():获取当前线程的对象

void remove():删除当前线程里的对象

initialValue():设置当前对象的默认初始值

ThreadLocal源码

以下是JDK1.5中的TheadLocal部分源码:

可以看到,源码中的方法与我们上述模拟的ThreadLocal简单版本一致。而在方法内部做了一些更加复杂的控制。

1.首先从类的成员变量有3个int类型的变量。其中threadLocalHashCode是类的实例变量,nextHashCode表示了下一个ThreadLocal实例的threadLocalHashCode的值,而HASH_INCREMENT是一个常量,表示每次threadLocalHashCode的增量。

threadLocalHashCode调用了nextHashCode()这个方法。这个方法的目的是将下一个hashCode的值赋值给了threadLocalHashCode,然后nextHashCode进行自增。

类的实例变量会在类每次实例化的时候初始化,所以每次实例化ThreadLocal的时候,它的threadLocalHashCode都会自增。

ThreadLocal是作为一个工具拿来给不同的对象来使用,为了区分不同的ThreadLocal的实例,所以需要定义threadLocalHashCode来区别不同的ThreadLocal实例。

2.在上面代码中,我们可以看到有一个ThreadLocalMap的类,这个类是ThreadLocal的内部类,受篇幅限制,并没有截图出来。这个内部类的实例却在Thread类中定义。

从上可以看到,每一个线程的实例的都有自己的成员变量threadLocals,也就是说是threadLocals这个对象依赖于每个线程存在。当调用set方法时,如果当前线程的threadLocals不为空,就把当前ThreadLocal实例做为key,要保持的对象做为值,设置到当前线程的ThreadLocalMap中;如果没有threadLocals,就创建并且设置。调用get方法的时候,从当前ThreadLocal对象和当前线程,取得对应的缓存对象。

ThreadLocal类通过操作每一个线程特有的ThreadLocalMap对象,从而实现了变量访问在不同线程中的隔离。因为每个线程的变量都是自己特有的,完全不会有并发错误。在JDK1.5之后,放在ThreadLocal中的对象,都是泛型,这样可以避免使用Object类型的强制类型转化。

ThreadLocal在Hibernate中的使用

以下为Hibernate中使用ThreadLocal的范例,我们可以学习一下

public class HibernateUtil {

private static Log log = LogFactory.getLog(HibernateUtil.class);

//定义SessionFactory

private static final SessionFactory sessionFactory;

static {

try {

// 通过默认配置文件Hibernate.cfg.xml创建SessionFactory

sessionFactory = new Configuration().configure().buildSessionFactory();

} catch (Throwable ex) {

log.error("初始化SessionFactory失败!", ex);

throw new ExceptionInInitializerError(ex);

}

}

//创建线程局部变量session,用来保存Hibernate的Session

public static final ThreadLocal session = new ThreadLocal();

//获取当前线程中的Session

public static Session currentSession() throws HibernateException {

Session s = (Session) session.get();

// 如果Session还没有打开,则新开一个Session

if (s == null) {

s = sessionFactory.openSession();

session.set(s); //将新开的Session保存到线程局部变量中

}

return s;

}

public static void closeSession() throws HibernateException {

//获取线程局部变量,并强制转换为Session类型

Session s = (Session) session.get();

session.set(null);

if (s != null)

s.close();

}

}

示例是在开启数据库连接的session对象的过程。其实,所有类似于这种缓存对象的代码都一样。首先从ThreadLocal中取当前线程的对象,如果对象为null,就重新创建对象,并把创建好的对象放到ThreadLocal里面。ThreadLocal持有的对象能够保持各个线程之间对象的独立。

ThreadLocal使用步骤

1、在多线程的类(如ThreadDemo类)中,创建一个ThreadLocal对象threadXxx,用来保存线程间需要隔离处理的对象xxx。

2、在ThreadDemo类中,创建一个获取要隔离访问的数据的方法getXxx(),在方法中判断,若ThreadLocal对象为null时候,应该new()一个隔离访问类型的对象,并强制转换为要应用的类型。

3、在ThreadDemo类的run()方法中,通过getXxx()方法获取要操作的数据,这样可以保证每个线程对应一个数据对象,在任何时刻都操作的是这个对象。

本文作者:易泉梁(点融黑帮),耿直程序猿一枚,会写点代码,喜欢享受生活,上不了厅堂,下得了厨房,喜欢运动,排球(二传),羽毛球,乒乓球水平可以虐菜鸟.人其实是可以快乐地生活的,只是我们自己选择了复杂,选择了叹息!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值