利于ThreadLocal管理Hibernate Session

      在利用Hibernate开发DAO模块时,我们和Session打的交道最多,所以如何合理的管理Session,避免Session的频繁创建和销 毁,对于提高系统的性能来说是非常重要的,以往是通过eclipse的插件来自动完成这些代码的,当然效果是不错的,但是总是觉得不爽(没有读懂那些冗长 的代码),所以现在打算自己实现Session管理的代码。我们知道Session是由SessionFactory负责创建的,而 SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,那么 Session是否是线程安全的呢?很遗憾,答案是否定的。Session中包含了数据库操作相关的状态信息,那么说如果多个线程同时使用一个 Session实例进行CRUD,就很有可能导致数据存取的混乱,你能够想像那些你根本不能预测执行顺序的线程对你的一条记录进行操作的情形吗?
      在Session的众多管理方案中,我们今天来认识一种名为ThreadLocal模式的解决方案。
      早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新 的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单, 就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每 一个线程都完全拥有一个该变量。
      ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现(为了简单,没有考虑集合的泛型):

  1. public   class  ThreadLocal {   
  2.       private  Map values = Collections.synchronizedMap( new  HashMap());   
  3.       public  Object get() {   
  4.         Thread currentThread = Thread.currentThread();    
  5.         Object result = values.get(currentThread);    
  6.          if (result ==  null &&!values.containsKey(currentThread)) {   
  7.            result = initialValue();   
  8.            values.put(currentThread, result);    
  9.         }   
  10.          return  result;    
  11.      }   
  12.       public   void  set(Object newValue) {   
  13.         values.put(Thread.currentThread(), newValue);   
  14.      }   
  15.       public  Object initialValue() {   
  16.          return   null ;    
  17.      }   
  18. }   

      那麽具体如何利用ThreadLocal来管理Session呢?Hibernate官方文档手册的示例之中,提供了一个通过ThreadLocal维护Session的好榜样:

  1. public   class  HibernateUtil {   
  2.         public   static   final  SessionFactory sessionFactory;   
  3.         static  {   
  4.             try  {   
  5.                  sessionFactory =  new  Configuration().configure()   
  6.                                    .buildSessionFactory();   
  7.            }  catch  (Throwable ex) {   
  8.                  throw   new  ExceptionInInitializerError(ex);   
  9.            }   
  10.        }   
  11.         public   static   final  ThreadLocal session =    
  12.                                              new  ThreadLocal();   
  13.         public   static  Session currentSession()  throws  HibernateException {   
  14.                Session s = session.get();   
  15.                 if (s ==  null ) {   
  16.                      s = sessionFactory.openSession();   
  17.                      session.set(s);   
  18.                }   
  19.                 return  s;   
  20.        }   
  21.         public   static   void  closeSession()  throws  HibernateException {   
  22.                 Session s = session.get();   
  23.                  if (s !=  null ) {   
  24.                       s.close();   
  25.                 }   
  26.                 session.set( null );   
  27.        }   
  28. }  

     只要借助上面的工具类获取Session实例,我们就可以实现线程范围内的Session共享,从而避免了线程中频繁的创建和销毁Session实例。当然,不要忘记在用完后关闭Session。
     写到这里,想再多说一些,也许大多数时候我们的DAO并不会涉及到多线程的情形,比如我们不会将DAO的代码写在Servlet之中,那样不是 良好的设计,我自己通常会在service层的代码里访问DAO的方法。但是我还是建议采用以上的工具类来管理Session,毕竟我们不能仅仅考虑今天 为自己做什么,还应该考虑明天为自己做什么!

阅读更多
个人分类: 多线程
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭