• Hibernate学习笔记--使用ThreadLocal


    参考资料:

    http://blog.sina.com.cn/s/blog_7ffb8dd5010146i3.html

    http://lavasoft.blog.51cto.com/62575/51926/

    一、问题的提出
      
     我们知道Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一 个SessionFactory并从中获取Session实例,而Session不是线程安全的。Session中包含了数 据库操作相关的状态信息,那么说如果多个线程同时使用一个Session实例进行CRUD,就很有可能导致数据存取的混乱,你能够想像那些你根本不能预测 执行顺序的线程对你的一条记录进行操作的情形吗?

    二、 解决方案思路使用Threadlocal类集合
      早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新 的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单, 就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每 一个线程都完全拥有一个该变量。
      ThreadLocal这个类本身不是代表线程要访问的变量,这个类的成员变量才是。JDK1.5给ThreadLocal加了泛型功能,即是 ThreadLocal,这个泛型T即是要线程的本地变量。线程通过ThreadLocal的get和set方法去访问这个变量T。
      ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现(为了简单,没有考虑集合的泛型):

       public class ThreadLocal { 
             private Map values = Collections.synchronizedMap(new HashMap()); 
             public Object get() { 
               Thread currentThread = Thread.currentThread();  
                Object result = values.get(currentThread);  
                if(result == null&&!values.containsKey(currentThread)) { 
                   result = initialValue(); 
                   values.put(currentThread, result);  
                } 
               return result;  
            } 
            public void set(Object newValue) { 
               values.put(Thread.currentThread(), newValue); 
            } 
            public Object initialValue() { 
               return null;  
            } 
       }

    三、解决方案步骤

       1、在HibernateUtil类中我们需要定义一个静态的成员变量用于保存当前线程共用的Session  

      public class HibernateUtil {
        private static SessionFactory factory;
        // 使用ThreadLocal集合保存当前业务线程中的SESSION
        private static ThreadLocal threadlocal= new ThreadLocal();
    
        static {
            // 第一步:读取HIBERNATE的配置文件,读取hibernate.cfg.xml文件
            Configuration con = new Configuration().configure();
            // 第二步:创建服务注册构建器对象,通过配置对象中加载所有的配置信息,存放到注册服务中
            ServiceRegistryBuilder regBuilder = new ServiceRegistryBuilder()
                    .applySettings(con.getProperties());
            // 创建注册服务
            ServiceRegistry reg = regBuilder.buildServiceRegistry();
            // 第三步:创建会话工厂
            factory = con.buildSessionFactory(reg);
        }
    
       
        public static Session getLocalThreadSession() {
            Session s = threadlocal.get();// 获取当前线程下的SESSION
            if (s == null) {
                s = getFactory().getCurrentSession();// 获取当前线程中的SESSION,需在在Hibernate.cfg.xml文件,具体请看面的说明
                threadlocal.set(s);// 将当前SESSION放入到当前线程的容器中保存
            }
            return s;
        }
    
        
        public static void closeSession() {
            Session s = threadlocal.get();// 获取当前线程下的SESSION
            if (s != null) {
                // s.close();//这里无需将Session关闭,因为该Session是保存在当前线程//中的,线程执行完毕Session自然会销毁
                threadlocal.set(null);// 将当前线程中的会话清除
            }
        }
    
    }

    2、添加OpenSessionInViewFilter过滤器(不要忘了在Web.xml配置该过滤器

    public void doFilter(ServletRequest request, ServletResponse response,
              FilterChain filterChain) throws IOException, ServletException {
      Session s = HibernateUtil.getThreadLocalSession();
      Transaction t = null;
      try {
        // 开始事务
        t = s.beginTransaction();
        // 进入一系列的过滤链,处理相应的ACTION、业务逻辑及数据层
        filterChain.doFilter(request, response);
        // 提交事务
        t.commit();
      } catch (Exception e) {
      if (t != null)
         t.rollback();//出现异常回滚事务
      throw new RuntimeException(e.getMessage(), e);
      } finally {
         HibernateUtil.closeSession();
      }
    }

    ===========================================

    说明:关于getCurrentSession()方法:
        sessionFactory.getCurrentSession()获取当前线程中的Session, 当调用时,hibernate将session绑定到当前线程,事务结束后,hibernate将session从当前线程中释放,并且关闭 session。当再次调用getCurrentSession()时,将得到一个新的session,并重新开始这一系列工作。这样调用方法如下: Session session = HibernateUtil.getSessionFactory().getCurrentSession();

    getCurrentSession和openSession的区别:
    1、getCurrentSession创建的session会和绑定到当前线程,而openSession不会。
    2、getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭
    3、getCurrentSession需在在Hibernate.cfg.xml文件中添加配置:
      <property name="current_session_context_class">thread</property>

    四、总结

       这种解决方案的优缺点:

    1. 优点:使用ThreadLocal除了有避免频繁创建和销毁session的好处外, 还有一个特别大的好处,
    就是可以做到多线程的数据隔离, 可以避免多个线程同时操作同一个session

    2. 缺点:  如下图


    使用拦截器在响应返回时,又重复过滤了一次,延长了响应的时间(改进:我们可以把写在过滤器中的方法写在一个具体的类,用到的时候再调用)

    五:注意:

    ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题。ThreadLocal为每个线程的中并发访问的数据提供一个副本,通过访问副本来运行业务,这样的结果是耗费了内存,大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。

    ThreadLocal不能使用原子类型,只能使用Object类型。ThreadLocal的使用比synchronized要简单得多。

    ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别。

    1. synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。

    2. ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

    Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离

    当然ThreadLocal并不能替代synchronized,它们处理不同的问题域。Synchronized用于实现同步机制,比ThreadLocal更加复杂。

  • 相关阅读:
    WAF、流控设备、堡垒机
    IPS入侵防御系统
    IDS入侵检测系统
    OSI安全体系结构
    边界网关协议BGP
    路由选择协议(RIP/OSPF)
    路由协议之OSPF
    路由协议之RIP
    Social engineering tookit 钓鱼网站
    YII框架中的srbac权限管理模块的安全与使用(版本是1.1.20)
  • 原文地址:https://www.cnblogs.com/gnivor/p/4440776.html
Copyright © 2020-2023  润新知