Java中java.lang.ThreadLocal类不是一个线程的本地实现,而是线程局部变量,也许叫ThreadlocalVar更加合适。ThreaLocal使用非常简单,就是为使用该变量的线程提供一个变量值的副本,每个线程都可以独立的改变自己的副本,而不会与其他线程冲突。
从线程角度看,每个线程都保持一个对其线程局部变量的隐式引用,只要线程是活动并且ThreadLocal实例可访问。线程结束之后,相应的线程局部变量的所有副本都会被垃圾回收。
通过ThreadLocal存取的数据,总是与当前线程有关。Jvm为每个运行的线程,绑定了私有的本地实例存取空间,每一个线程都可以独立地改变空间内容而不会和其他线程副本冲突,从而隔离了线程,解决的并发访问问题。
ThreadLocal的实现思路很简单,在其内部有一个Map,用户存储每一个线程变量副本。
对已多线程资源共享问题,有两种解决方式,一种同步机制,以时间换空间,不同的线程排队访问。一种是ThreadLocal,以空间换时间,为每个线程都复制一份共享资源的副本,线程直接访问自己的副本,因此可以同时访问互不影响。
ThreadLocal主要由四个方法组成initialValue(),get(),set(T),remove(),其中值得注意的是initialVlaue(),该方法是一个protected的方法,显然是为了子类重写而特意实现的。该方法返回当前线程在该线程局部变量的初十值。这是方法是一个延时调用的方法,在一个线程第一次调用get()或者set(object )时才执行,丙炔仅执行1次。ThreadLocal中确实实现直接返回一个null;
使用ThreadLocal的典型实例就是Hibernate中的session工具类HibernateUtil,该类用于session管理。
public class HibernateUtil {
private static Log log = LogFactory.getLog(HibernateUtil.class);
private static final SessionFactory 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
* @return Session
* @throws HibernateException
*/
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();
}
}
在这个类中,由于没有重写ThreadLocal的initialValue()方法,则首次创建线程局部变量session的初始值为null,第一次调用currentSesion()的时候,线程局部变量的get()方法也为null。因此,对session做判断,如果为null,则打开一个新的会话,并保存线程局部变量session中,这一步非常关键,这也是“public static final ThreadLocal seesion = new ThreadLocal()”所创建对象session能强制转换为Hibernate Session对象的原因。
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
下面是自己写的一个小例子,创建一个Bean,通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立性。
public class Bean {
private int num;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
public class ThreadLocalDemo implements Runnable {
//创建线程局部变量,用来保存bean
private static final ThreadLocal<Bean> beanLocal = new ThreadLocal<Bean>();
public Bean getBean(){
//获取线程本地变量
Bean bean = beanLocal.get();
//如何还没有设置,则设置。(第一次肯定为空)
if(bean==null){
//将新建的bean保存到线程本地变量
bean = new Bean();
beanLocal.set(bean);
}
return bean;
}
public void test(){
//获取当前线程名字
String threadName = Thread.currentThread().getName();
System.out.println("currThreadName:"+threadName);
//生成随机数
Random random = new Random();
int num = random.nextInt(10);
//获取当前线程本地变量bean,并将num设置到bean变量中
Bean bean = getBean();
bean.setNum(num);
System.out.println("threadName"+threadName+",BeanInt:"+num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("currThreadName:"+threadName+", currrThreadBeanName:"+bean.getNum());
}
@Override
public void run() {
test();
}
public static void main(String[] args){
ThreadLocalDemo demo = new ThreadLocalDemo();
Thread td1 = new Thread(demo,"demo1");
Thread td2 = new Thread(demo,"demo2");
td1.start();
td2.start();
}
}