问题描述
书上有一个工具类HibernateUtil,书本解释它的作用是:“将HibernateSession存放在一个ThreadLocal变量中,对于同一个线程的请求,将可以轻松访问该Session”“可以保证将线程不安全的Session绑定限制在当前线程内——也就是实现‘上下文相关的’Session”我不明白的是——既然从SessionFactory.openSession()获取Session不是单例模式,也就是每次获取Session都是不同的,那么为什么会出现多个线程同时访问同一个Session的问题呢?比如,在一个Web应用中,在处理用户请求的Servlet中通过SessionFactory.openSession()获取一个Session,然后访问数据库,最后关闭Session,按理说每次用户请求所用到的Session应该是不同的,应该不会出现多个线程同时访问同一个Session造成的线程安全问题啊,为什么要用工具类HibernateUtil呢??不解~~恳求各位高手慷慨解惑,万分感激!!!
解决方案
解决方案二:
这是个很好的问题!!!顶!!!
解决方案三:
共同寻解...
解决方案四:
我想我一定是理解错了什么东西,期待高手解惑。也许您一句话,就可以惊醒我这个“梦中人”,很有成就感的,呵呵~~
解决方案五:
嗯楼主是个很是在的家伙,属于圣斗士那类型的,不错,就凭这股干劲儿,相信你在今后会大牛!!
解决方案六:
嗯楼主是个很是在的家伙,属于圣斗士那类型的,不错,就凭这股干劲儿,相信你在今后会大牛!!====================================================================================谢谢fpy_061625的夸奖,对于编程其实我只是个菜鸟,很惭愧!我只是觉得:搞编程,学任何一门技术、工具(比如Hibernate),理解其实现原理、思想是最重要的,反而语法、配置、操作是其次——当你理解了其实现原理、思想,就不会被它那些纷繁复杂的配置、操作所迷惑。理解了就不会觉得它很神秘:“哦,这种设计如果让我来搞,也许我也会这样想”,还有可能发现它的不足从而改进它,甚至有必要时自己开发一套更加先进的工具;反之如果不理解,即使对语法、配置、操作烂熟于心,它都不是你的,人家工具怎么改怎么变,你只能跟着走,自己没有任何主动权。我本身是读数学的,也许还带着这样一种习惯——面对一大堆纷繁复杂的抽象符号、式子、数字、曲线,如果你头脑中对其背后的思路、原理有个很清晰的概念,就永远不会被它疑惑,否则见到就头疼。第一次发帖提问,希望高手们可以多多指教,感激不尽!当人也希望结识有相同兴趣、志向的朋友。
解决方案七:
提问中提到的Web应用的例子,为了简单起见,我省掉了Dao层等东西,访问数据库直接在控制器Servlet里面进行,实际项目应该很少这个样子。
解决方案八:
http://caoyinghui.javaeye.com/blog/523902这个地址上写着不错,你可以看看
解决方案九:
对于SessionFactory.openSession()来说,同一个线程内不管做多少次session1=SessionFactory.openSession();session2=SessionFactory.openSession();session1=session2;创建的session都是相等的,这样保证了session内操作数据的完整性和隔离性。反之则不相当
解决方案十:
对于SessionFactory.openSession()来说,同一个线程内不管做多少次session1=SessionFactory.openSession();session2=SessionFactory.openSession();session1=session2;创建的session都是相等的,这样保证了session内操作数据的完整性和隔离性。反之则不相当===================================================================================谢谢回复!但是我尝试过,在这种情况下执行System.out.println(session1==session2),结果等于false,说明两次获取的session是不同的。而且我的意思是:想看看有什么情况会出现多个线程同时共享一个session问题,因为我想搞明白那个工具类HibernateUtil的存在究竟有什么意义,目前我想不到任何一定要用到它的例子。
解决方案十一:
引用7楼wzju64676266的回复:
http://caoyinghui.javaeye.com/blog/523902这个地址上写着不错,你可以看看
================================================================================谢谢推荐!但我粗略看了一下,那帖子是介绍怎样实现保证“一个线程上最多只能创建一个session实例.并且每个线程都能创建一个实例”的问题,没有提到怎样会出现多个线程同时访问一个session的问题。
解决方案十二:
顶顶
解决方案十三:
个人觉得SessionFactory.openSession()每次获取的session是不同的,但在servlet中存在线程安全问题,如果把session声明为servlet的实例变量,就会出现多个线程访问一个session的情况。
解决方案十四:
引用12楼fudoublelong的回复:
个人觉得SessionFactory.openSession()每次获取的session是不同的,但在servlet中存在线程安全问题,如果把session声明为servlet的实例变量,就会出现多个线程访问一个session的情况。
首先非常感谢fudoublelong朋友的回复!“把session声明为servlet的实例变量,就会出现多个线程访问一个session的情况”——这个很容易理解,这样相当于多个线程同时访问同一个Servlet实例,那么当然也共享它的成员变量(属性)session,毫无疑问。但问题是为什么要这样做?为什么不把session声明为Servlet方法的局部变量(此时相当于要用session实例时就创建、用完就关闭、销毁)?这样做不是很方便且节省内存吗??会有什么负面作用??(好像创建session实例代价开销不大)盼指教!
解决方案十五:
没用过hibernate,胡说一下hibernate使用ThreadLocal,主要是为了“‘上下文相关的’Session”也就是说,使用Threadlocal对当前进程进行判断,如果是有[这个线程的session]就直接返回。而lz担心的“多个线程”,可能就有了多了session了。随便胡说一下,等待被拍砖bdgoodluck
解决方案:
引用14楼villagehead的回复:
没用过hibernate,胡说一下hibernate使用ThreadLocal,主要是为了“‘上下文相关的’Session”
谢谢回复!大家交流,不存在什么“胡说”,只要多交流即使没有最终的结果,都会有收回的。但可能你误解了我的疑问,我不是对ThreadLocal的作用有什么怀疑。
解决方案:
比如,在一个Web应用中,在处理用户请求的Servlet中通过SessionFactory.openSession()获取一个Session,然后访问数据库,最后关闭Session,按理说每次用户请求所用到的Session应该是不同的,应该不会出现多个线程同时访问同一个Session造成的线程安全问题啊,为什么要用工具类HibernateUtil呢??不解~~如果你的session在你的Servlet是实例变量,那你的多个线程访问的session对象肯定是同一个对象,这样就会出错了。
解决方案:
引用16楼dope2002的回复:
比如,在一个Web应用中,在处理用户请求的Servlet中通过SessionFactory.openSession()获取一个Session,然后访问数据库,最后关闭Session,按理说每次用户请求所用到的Session应该是不同的,应该不会出现多个线程同时访问同一个Session造成的线程安全问题啊,为什么要用工具类HibernateUtil呢??不解~~如果你的session在你的Servlet是实例变量,那你的多个线程访问的session对象肯定是同一个对象,这样就会出错了。
谢谢您的回复!上面12楼的朋友也提到您所说的这个问题,但我还是有疑问——为什么要将session设置为Servlet的实例变量?为什么不把session声明为Servlet方法的局部变量(此时相当于要用session实例时就创建、用完就关闭、销毁)?这样做既可避免发生多个线程同时访问一个Session带来的线程安全问题,也很方便,又节省内存,会有什么负面作用吗??(好像创建session实例的代价、开销不大)
解决方案:
ThreadLocal保证不同的线程有不同的Session的.
解决方案:
引用18楼obullxl的回复:
ThreadLocal保证不同的线程有不同的Session的.
谢谢回复!但我的疑问不是ThreadLocal作用的问题。我的疑问是为什么会出现多个线程同时访问同一个Session的情况,如果可以简单地避免这种情况(比如像我所说的把Session声明为局部变量,要用就建,用完立刻销毁,根本不会出现多线程访问冲突),又没什么负面作用的,那么要这个ThreadLocal有什么用??
解决方案:
顶顶
解决方案:
System.out.println(session1==session2)当然是false的,这个是基本概念里的了,两个对象的相等判断,你这样肯定是false先不说他们是否是一个session,就算是,那也只能说是两个不同的引用,指向同一个session对象了,本身session1和session2是两个不同的对象
解决方案:
顶,学习了。
解决方案:
想了很久也想不出来多个线程同时访问同一个Session的情况。等待牛人解答。对于ThreadLocal的作用,我觉得只是为了避免一个线程中过于频繁的openSession和closeSession,在Web应用中,可以在filter中统一管理。
解决方案:
多个线程访问同一个Session就是一个常见的事情,你用一个frameset,里面包含3个frame,那么IE就同时向服务器发出三个请求,这三个请求都是同一个session的了!而每个请求就是一个线程处理的,所以多个线程访问同一个Session是非常常见的事情。
解决方案:
楼上说的是http的session?
解决方案:
ThreadLocal的用处在于不需要将Hibernate的Session逐层传递。而如果你每次在访问时都打开新的Hibernate的Session,那么事务等都不方便进行处理。所以很多时采用的是每一次Http请求只打开一个Hibernate的Session及事务。使用ThreadLocal就可以做到这一点
解决方案:
引用17楼cjgg2000的回复:
引用16楼dope2002的回复:比如,在一个Web应用中,在处理用户请求的Servlet中通过SessionFactory.openSession()获取一个Session,然后访问数据库,最后关闭Session,按理说每次用户请求所用到的Session应该是不同的,应该不会出现多个线程同时访问同一个Session造成的线程安全问题啊,为什么要用工具类HibernateUtil呢??不解~~如果你的session在你的Servlet是实例变量,那你的多个线程访问的session对象肯定是同一个对象,这样就会出错了。谢谢您的回复!上面12楼的朋友也提到您所说的这个问题,但我还是有疑问——为什么要将session设置为Servlet的实例变量?为什么不把session声明为Servlet方法的局部变量(此时相当于要用session实例时就创建、用完就关闭、销毁)?这样做既可避免发生多个线程同时访问一个Session带来的线程安全问题,也很方便,又节省内存,会有什么负面作用吗??(好像创建session实例的代价、开销不大)
如果作为局部变量,照你说的这个流程当然可以。但是在高并发的情况下,会影响性能。(好像创建session实例的代价、开销不大)//创建session的开销在高并发情况下是很大的。而用thread可以解决这个问题:“将HibernateSession存放在一个ThreadLocal变量中,对于同一个线程的请求,将可以轻松访问该Session”当同一个线程再次请求时,只需从threadlocal中获取而无需再创建。
解决方案:
很欣赏楼主的学术精神:)不知道我是不是正确理解了楼主的疑问。我想你的疑问是:既然SessionFactory.openSession()每次获得的都是不同的对象,那就根本不存在多线程访问冲突的问题,为什么还要搞ThreadLocal之类的东西来解决这个所谓的问题?如果是这个问题,那我是这样理解的:“多线程访问冲突”的问题,并不是一开始需要解决的问题,而是为了解决另外一个问题时产生的问题。真正要解决的是“如何在程序处理过程中随时能得到一个session对象,用于数据库操作”。一个request被servlet处理的过程中,会穿透很多层代码,用参数传递显然不方便,所以要能够“随时得到”,但同时又希望整个过程中得到的都是同一个session对象,这样才方便事务控制等要求。那么怎么办呢?“单例”和“全局变量”都可以达到这个目标,但都会产生“多线程访问冲突”的问题。于是就有了ThreadLocal这种类似于“全局变量”的方法。
解决方案:
引用28楼maquan的回复:
很欣赏楼主的学术精神 :)不知道我是不是正确理解了楼主的疑问。我想你的疑问是:既然SessionFactory.openSession()每次获得的都是不同的对象,那就根本不存在多线程访问冲突的问题,为什么还要搞ThreadLocal之类的东西来解决这个所谓的问题?如果是这个问题,那我是这样理解的:“多线程访问冲突”的问题,并不是一开始需要解决的问题,而是为了解决另外一个问题时产生的问题。真正要解决的是“如何在程序处理过程中随时能得到一个session对象,用于数据库操作”。一个request被servlet处理的过程中,会穿透很多层代码,用参数传递显然不方便,所以要能够“随时得到”,但同时又希望整个过程中得到的都是同一个session对象,这样才方便事务控制等要求。那么怎么办呢?“单例”和“全局变量”都可以达到这个目标,但都会产生“多线程访问冲突”的问题。于是就有了ThreadLocal这种类似于“全局变量”的方法。
我感觉您是理解了lz的问题了,可是您回答的还是拐到了“为什么使用ThreadLocal”上。也不是lz的核心问题-“会不会有情况遇到多线程冲突”和“什么情况下会遇到”占地学习bdgoodluck
解决方案:
引用29楼villagehead的回复:
我感觉您是理解了lz的问题了,可是您回答的还是拐到了“为什么使用ThreadLocal”上。也不是lz的核心问题-“会不会有情况遇到多线程冲突”和“什么情况下会遇到”
那我就再补充两句:p为了解决“如何在程序处理过程中随时能得到同一个session对象,用于数据库操作”的问题,如果我们只是简单地采用了“单例”或者“全局变量”的方法,那立刻就会出现“多线程访问冲突”的问题了。还是那句话,这个“多线程访问冲突”的问题并不是原本就存在的问题,而是为了解决另一个问题而可能产生出来的问题。
解决方案:
也就是捎带手儿的把“多线程访问冲突”也解决了?也就是“搂草打兔子”喽?一股寒意...goodluck
解决方案:
引用31楼villagehead的回复:
也就是捎带手儿的把“多线程访问冲突”也解决了?也就是“搂草打兔子”喽?一股寒意...goodluck
可能是我们的语言表达习惯有差异。我前面说的内容中绝对没有捎带手儿的把“多线程访问冲突”也解决了的意思,恰恰相反,原本根本就不存在“多线程访问冲突”的问题。另外,你的寒意从何而来?不会是属兔子的吧?hehe,justkidding:)
解决方案:
应该说Session使用了单例模式的,所以其实还是一个session对象
解决方案:
其实产生这个问题的根源在于,我们不希望经常性的打开、关闭Hibernate的Session对象,而是每一次Http请求只产生一个Session所以需要将这个openSession返回的Session对象保存在某个变量中,以方便下一个方法继续使用但是要放哪里呢?如果放在一个类的静态变量中,那么另外一个请求也会意外的得到了这个Hibernate的Session进行操作了,所以这是不可行的。而放在一个静态变量ThreadLocal中,则可以另外一个请求(肯定与当前请求所在线程不同)同样访问ThreadLocal的时候,ThreadLocal可以保证当前请求与另外一个请求是不会发生冲突和混淆的,而是相互独立的(即当前请求set(sessionA)以后,另外那个请求get是取不出sessionA的)所以通常会用ThreadLocal来作为存储Hibernate的Session对象
解决方案:
引用32楼maquan的回复:
引用31楼villagehead的回复:也就是捎带手儿的把“多线程访问冲突”也解决了?也就是“搂草打兔子”喽?一股寒意...goodluck可能是我们的语言表达习惯有差异。我前面说的内容中绝对没有捎带手儿的把“多线程访问冲突”也解决了的意思,恰恰相反,原本根本就不存在“多线程访问冲突”的问题。另外,你的寒意从何而来?不会是属兔子的吧?hehe,justkidding:)
估计lz就是要看这句“原本根本就不存在“多线程访问冲突”的问题。”我寒是因为...现在是冬天。...哈哈goodluck
解决方案:
引用26楼chdw的回复:
ThreadLocal的用处在于不需要将Hibernate的Session逐层传递。而如果你每次在访问时都打开新的Hibernate的Session,那么事务等都不方便进行处理。所以很多时采用的是每一次Http请求只打开一个Hibernate的Session及事务。使用ThreadLocal就可以做到这一点
感谢回复!您的回答对我很有启发意义,我有点理解ThreadLocal在此处的意义了,不错!!
解决方案:
引用27楼dope2002的回复:
引用17楼cjgg2000的回复:引用16楼dope2002的回复:比如,在一个Web应用中,在处理用户请求的Servlet中通过SessionFactory.openSession()获取一个Session,然后访问数据库,最后关闭Session,按理说每次用户请求所用到的Session应该是不同的,应该不会出现多个线程同时访问同一个Session造成的线程安全问题啊,为什么要用工具类HibernateUtil呢??不解~~如果你的session在你的Servlet是实例变量,那你的多个线程访问的session对象肯定是同一个对象,这样就会出错了。谢谢您的回复!上面12楼的朋友也提到您所说的这个问题,但我还是有疑问——为什么要将session设置为Servlet的实例变量?为什么不把session声明为Servlet方法的局部变量(此时相当于要用session实例时就创建、用完就关闭、销毁)?这样做既可避免发生多个线程同时访问一个Session带来的线程安全问题,也很方便,又节省内存,会有什么负面作用吗??(好像创建session实例的代价、开销不大)如果作为局部变量,照你说的这个流程当然可以。但是在高并发的情况下,会影响性能。(好像创建session实例的代价、开销不大)//创建session的开销在高并发情况下是很大的。而用thread可以解决这个问题:“将HibernateSession存放在一个ThreadLocal变量中,对于同一个线程的请求,将可以轻松访问该Session”当同一个线程再次请求时,只需从threadlocal中获取而无需再创建。
感谢回复!您的回答对我也很有用!我明白了Threadlocal的意义了,不会再纠缠于“会否有多线程并发一个session的问题”。
解决方案:
引用28楼maquan的回复:
很欣赏楼主的学术精神 :)不知道我是不是正确理解了楼主的疑问。我想你的疑问是:既然SessionFactory.openSession()每次获得的都是不同的对象,那就根本不存在多线程访问冲突的问题,为什么还要搞ThreadLocal之类的东西来解决这个所谓的问题?如果是这个问题,那我是这样理解的:“多线程访问冲突”的问题,并不是一开始需要解决的问题,而是为了解决另外一个问题时产生的问题。真正要解决的是“如何在程序处理过程中随时能得到一个session对象,用于数据库操作”。一个request被servlet处理的过程中,会穿透很多层代码,用参数传递显然不方便,所以要能够“随时得到”,但同时又希望整个过程中得到的都是同一个session对象,这样才方便事务控制等要求。那么怎么办呢?“单例”和“全局变量”都可以达到这个目标,但都会产生“多线程访问冲突”的问题。于是就有了ThreadLocal这种类似于“全局变量”的方法。
感谢回复!我对您的回答感到非常满意!!您已经很理解我的疑惑,gotthepoint!并且回答得很详细、很到位,我明白这个ThreadLocal的作用了,再次感谢!还有几个相关的问题想请教,(1)session.beginTransaction()、session.commit()是不是通知数据库执行一次相应的“开启事务”和“提交事务”的指令??因为据我所知道,应用程序事务和数据库事务并不一定是一一对应的(2)如果(1)的答案是肯定的话,那么session.beginTransaction()、session.commit()之间的代码(执行时间)应该尽可能地短,否则会阻止数据库的并发修改从而影响网站性能,对吗?(3)在一个线程中,每次要用到ThreadLocal中的Session的时候,都应该是:开启事务-->持久化操作-->提交事务,而无须每次都关闭Session,Session留待最后一次用完后才关闭,对吗??若能回答,感激不尽!!
解决方案:
引用38楼cjgg2000的回复:
还有几个相关的问题想请教,(1)session.beginTransaction()、session.commit()是不是通知数据库执行一次相应的“开启事务”和“提交事务”的指令??因为据我所知道,应用程序事务和数据库事务并不一定是一一对应的(2)如果(1)的答案是肯定的话,那么session.beginTransaction()、session.commit()之间的代码(执行时间)应该尽可能地短,否则会阻止数据库的并发修改从而影响网站性能,对吗?(3)在一个线程中,每次要用到ThreadLocal中的Session的时候,都应该是:开启事务-->持久化操作-->提交事务,而无须每次都关闭Session,Session留待最后一次用完后才关闭,对吗??若能回答,感激不尽!!
看到楼主对每个回帖都认真总结、回应,我很感动^_^你说的这三个问题,我没有十足的把握,尽我所知说一下吧,抛砖引玉。(1)回答这个问题最彻底的办法是阅读Hibernate的源代码。我现在没去读源代码,凭感觉,这个可能跟具体的实现有关,最简单的实现当然就是直接执行数据库对应的操作而已。但就像你说的,应用程序事务和数据库事务并不一定是一一对应的,举个例子来说,如果启用了“外部事务服务”的话,在一个“事务”中可能就不仅仅是一个数据库的操作,而可能有多个数据库的联动操作,甚至可能有文件系统操作,等等。(2)这是个一般性的要求,永远是正确的:)不过也不是绝对的,如果真有复杂的更新操作,也可以想别的办法来使得写操作不会长时间阻塞读操作,比如通过replication的方式来部署多个数据库实例,一写多读。(3)差不多是这个意思。不过更准确地说,连“开启事务”和“提交事务”也不是每次一定要做的,根据需要进行配置,有可能就要做成“最开始开启一次,最后提交一次”形式的。
解决方案:
引用39楼maquan的回复:
(2)这是个一般性的要求,永远是正确的:)不过也不是绝对的,如果真有复杂的更新操作,也可以想别的办法来使得写操作不会长时间阻塞读操作,比如通过replication的方式来部署多个数据库实例,一写多读。
sorry,刚刚注意到楼主的问题中说的是“并发修改”的问题,我回答的是“读写冲突”的问题,文不对题,忘记它吧。并发修改确实可能成为问题,如果多个修改操作是冲突的,肯定得一个等另一个了,要是不直接冲突的话,也许问题不大。不管怎么说,“尽可能缩短事务中操作所耗费的时间”总是没错的:)
解决方案:
这个得问写出了那段代码的程序员
解决方案:
楼主把那个HibernateUtil的代码帖出来嘛,看看他究竟为什么那样说
解决方案:
引用39楼maquan的回复:
引用38楼cjgg2000的回复:还有几个相关的问题想请教,(1)session.beginTransaction()、session.commit()是不是通知数据库执行一次相应的“开启事务”和“提交事务”的指令??因为据我所知道,应用程序事务和数据库事务并不一定是一一对应的(2)如果(1)的答案是肯定的话,那么session.beginTransaction()、session.commit()之间的代码(执行时间)应该尽可能地短,否则会阻止数据库的并发修改从而影响网站性能,对吗?(3)在一个线程中,每次要用到ThreadLocal中的Session的时候,都应该是:开启事务-->持久化操作-->提交事务,而无须每次都关闭Session,Session留待最后一次用完后才关闭,对吗??若能回答,感激不尽!!看到楼主对每个回帖都认真总结、回应,我很感动^_^你说的这三个问题,我没有十足的把握,尽我所知说一下吧,抛砖引玉。(1)回答这个问题最彻底的办法是阅读Hibernate的源代码。我现在没去读源代码,凭感觉,这个可能跟具体的实现有关,最简单的实现当然就是直接执行数据库对应的操作而已。但就像你说的,应用程序事务和数据库事务并不一定是一一对应的,举个例子来说,如果启用了“外部事务服务”的话,在一个“事务”中可能就不仅仅是一个数据库的操作,而可能有多个数据库的联动操作,甚至可能有文件系统操作,等等。(2)这是个一般性的要求,永远是正确的:)不过也不是绝对的,如果真有复杂的更新操作,也可以想别的办法来使得写操作不会长时间阻塞读操作,比如通过replication的方式来部署多个数据库实例,一写多读。(3)差不多是这个意思。不过更准确地说,连“开启事务”和“提交事务”也不是每次一定要做的,根据需要进行配置,有可能就要做成“最开始开启一次,最后提交一次”形式的。
非常感谢你再次回复!但总感觉您说的很到到位、很全面,虽然我不一定全部能理解,给我很大启发,佩服!!虽然我不能一一道谢,其实我真的很感激所有回复的朋友,不是客气,而是真的发自内心的——因为大家都做过菜鸟,能够理解我的心情:遇到困扰自己很久的问题很郁闷,得到别人指点迷津豁然开朗的那种畅快,对解惑者真的是由衷感激的!交流真的很重要,再次谢谢所有回复的朋友!!!
解决方案:
“但总感觉您说的很到到位、很全面,虽然我不一定全部能理解,给我很大启发,佩服!!”也许是有点激动,中文都说得有点乱,上面那句应改为——“总感觉您说的很到到位、很全面,虽然我不一定全部能理解,但给我很大启发,佩服!!”呵呵~~
解决方案:
顶楼主!
解决方案:
我认为session不能共享,我觉得一个请求就是建立一个session连接,如果共享了,线程1把session关闭了,那其他几个线程就不能对session操作了。我今天也是对多线程,单实例疑惑,上面这个是我个人看法,望各位给指导指导。。。。
解决方案:
因为你可以写代码实现多个线程访问同一个SESSION
解决方案:
将HibernateSession存放在一个ThreadLocal变量中那就是说明了每对对象访问的时候为其出创建了一个session的副本,所以会不相等但是都是同一个对象的副本
解决方案:
我的理解:把session放在ThreadLocal中的目的是:不用显示地在本次请求中传递session,简化代码。而不是为了控制多线程问题。sf好像还有个方法叫xxxxCurrentSession(),可以返回不同的session。
解决方案:
-----------------12楼是对的。