Session session = SessionFactory.getSession();
这一步加final的意思是session这个引用对象只能指向SessionFactort.getSession()传回的这个对象,之后其指向的对象地址不能再次改变,不加final是可以再次赋值(即再次改变其指向)的。而加不加final,session所指向的这个对象的内在属性是完全可以改变的,甚至可以模拟两个线程,同时调用其方法,改变这个对象的设置。所以加final与线程安全与否几乎没有关系。
方法中的局部变量会随着方法的调用而新建和释放,因此没有线程安全之忧,有担忧的确是是类变量,不过这也要看情况。类的实例对象在正常使用的情况下也不存在线程问题(不同线程不同对象),但某些情况就会有问题,比如servletClass的实例,请求第一次到达后web容器会实例化对应的servletClass,而此后无论再有多少类似请求,都会使用第一次实例化的这个对象(除非长期没有这个请求,web容器有可能会销毁这个对象)而http请求是有并发现象存在的,操作的又是同一个对象,当然会出现并发操作同一个对象中的同一个类变量的问题,这就是线程不安全的一个例子。
SessionFactory在方法中创建Session,并返回给调用端,当然不存在线程问题,当然能保证为不同地点,不同线程的调用者提供不同的Session,而Session一旦创建,就要看调用者如何使用了,把它当做类变量使用,而又把这个类的实例供多个线程操作,而又不加排它锁,当然会出线程安全的问题。通常在控制单元(servletClass/struts的action)使用hibernateSession或jdbc的connection时,都不建议把它作为类变量来用
“在不使用ThreadLocal维护session的情况下。为什么说Session是非线程安全的。是否是因为它存在很多类级变量吗?如果确实如此的话,那么SessionFactory尽管是是线程安全的。但它同样也存在很多类级的非final成员实例变量,如:private transient boolean isClosed = false;private transient SchemaExport schemaExport;这难道不会影响线程安全么?”
这个问题应该从两个角度说:
首先,我认为楼主需要弄清楚的是final关键字可以修饰多种目标,final可以修饰非抽象类、非抽象类成员方法和变量。
如果是修饰类,那么final类不能被继承,没有子类,final类中的方法默认是final的。
如果是修饰方法,那么final方法不能被子类的方法覆盖,但可以被继承。
如果是修饰成员变量,那么final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
另外,final不能用于修饰构造方法。
由此可见,SF(SessionFactory)中的确已经将绝大部分的成员变量设计成了final的,因此,确实不存在并发冲突的问题,而对仅有的几个非final成员变量的操作也并非在SF类中提供,因此,SF确实是现成安全的。反观Session,transient是和序列化相关的一个修饰符,和线程安全无关,因此,Session中的大部分成员变量都复发避免并非访问冲突的可能。因此,Session确实不能认为是线程安全的。
其次,线程安全和线程敏感这两个概念我个人认为应该区分一下,ThreadLocal实现的只是一种线程敏感特性,也就是说,Hibernate2中需要用Util包中的方法获得和线程相关的Session,而到了Hibernate3,尽管SessionFactory中已经整合好了ThreadLocal的特性,这主要是依靠CurrentSessionContext接口的不同实现类(如JPA,Hibernate等实现模式)来从一个大的SessionMap中获得和线程相关的Session(这种Session一般称为线程敏感的),但是,Session自身依然是非线程安全的,原因见前面“首先”部分的分析。这里引入“其次”的分析主要是想避免将线程安全和线程敏感混淆。
最后,10楼的哥们第一段和最后一段说的很多,第一段中说的final的位置楼主不要认为是修饰成员变量,他说的final的位置是修饰整个实例对象,这个时候由于实例对象属于引用对象,因此考虑到堆内存和栈内存的关系,实际对象依然可以被不同线程改变,因此,在实例对象前加final修饰符并不能代表这个对象就是线程安全的。
10楼的最后一段说的也很好,其实之所以说要认识到Session是非线程安全的,主要是提醒大家,在自己开发的调用类中,不要将Session设置为成员变量,而是要将Session写在方法中,这样得到的Session才是因线程而异的,如果放在类中,只随着Servlet初始化时构建一次,那么今后并发冲突时肯定难免的了。
isClose()确实被SF实现类提供,是暴露的,但是,不是说一个类暴露了变量,那么这个类就一定是不安全的,这里所谓的安全主要是指是否能有效控制并发。而所谓并发控制首先需要明确的是边界,并发控制的边界我认为是:"从你执行‘读’到你执行‘写’这一过程中有没有其它的人‘写入’?" 由于SF的open与close基本上是托管给容器的,因此这个变量的状态不是一个线程可以改变的,所以,这样的变量并不涉及并发冲突。因此也不影响线程安全。
另外,并发控制一般靠锁来实现,他的核心思想是解决‘读’完之后谁能‘写’,而事物隔离级别解决的是‘写’完之后谁能‘读’。他们分别体现的是ACID中的C和I,是不同的概念。有没有其它的人写?”
综上,一个暴露的变量如果从功能设计上的目的只是为了提供‘读取’的目的,那么即使暴露也不会产生任何预期的并发冲突。
SessionFactoryImpl 显然不是严格语义上的thread safe,那么多getXXX()都暴露在外面,客户程序可以随意put。
为什么java doc里面说的“It is crucial that the class is not only thread safe, but also highly concurrent.” ?
那是因为有些变量虽然可以改,但是不会影响功能,因为在constructor的时候变量已经从Configuration里面解析好了,譬如:SessionFactoryImpl.setting。
再者有些变量已经处理过了,你get了也没法改。譬如:SessionFactoryImpl.classMetadata,在constructor的时候已经classMetadata = Collections.unmodifiableMap(classMeta);了
也就是说,‘写入’这个功能不是线程提供的,而是构造方法,托管容器,甚至Aspect功能来实现的。
hibernate中Session的线程安全的讨论
原创
©著作权归作者所有:来自51CTO博客作者mb648972af0d702的原创作品,请联系作者获取转载授权,否则将追究法律责任
下一篇:excel中默认时间格式修改
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
java 线程安全的int
java 线程安全的int
线程安全 原子变量 Java -
多线程这些线程安全的坑,你在工作中踩了么?
多线程这些线程安全的坑,你在工作中踩了么?
线程池 System 死锁 -
Spring中hibernate的分页方法Callback讨论
Spring和Hibernate结合的时候,接口提供非常有限尤其是对于一些需要自定义对数据进行更改的操作时,Spring提供的update和save就远远无法满足复杂的业务需求了
Spring Hibernate DAO iBATIS hibernate