论坛首页 Java企业应用论坛

LockMode.UPGRADE 引发的对于Hibernate事务的思考

浏览 8286 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-05-22  
应用场景:现有一个表Article(帖子),其中有一个字段visit_amount表示这个帖子被浏览过多少次。
现在做一个实验,开两个线程做同一件事情,就是:取出原有的浏览次数,然后加一,再存回数据库。每个线程把这个动作重复10次。

设想的结果:如果浏览次数的初始值是0的话,那么程序运行完毕后,浏览次数应该增长到20。


package com.javaye;

import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.javaye.models.Article;

public class Main {
	

	private  static void update(){
		Session session = HibernateSessionFactory.getSession();
		Transaction tx =  session.beginTransaction();
		Article art = (Article) session.load(Article.class, 1);
		art.setVisitAmount(art.getVisitAmount()+1);
		session.save(art);
		tx.commit();
		
		
	}
	
	private static void work(){
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+":"+(i+1)+"times.");
			update();
		}
	}
	
	public static void main(String[] args) throws Exception{
		Thread t1 = new Thread(
			new Runnable(){
				public void run(){
					work();
				}
			}
		);
		
		Thread t2 = new Thread(
				new Runnable(){
					public void run(){
						work();
					}
				}
		);
		t1.setName("Thread1");
		t2.setName("Thread2");
		t1.setDaemon(true);
		t2.setDaemon(true);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		
	}
}



上面是第一个版本,结果测试的结果发现没有实现事务的隔离性,每次的结果是随机的,要么是10要么是11或12。

然后,我进行了修改,加上了session.evict(art)来清空缓存,然后发现结果变得好一些了,但是结果并不总是20,有时候是19或18。真奇怪。

再后来,把load语句改为:
Article art = (Article) session.load(Article.class, 1,LockMode.UPGRADE);


经过多次测试,结果总为20,实现了事务的隔离性。

最终work的代码如下:
package com.javaye;

import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.Transaction;

import com.javaye.models.Article;

public class Main {
	
	private static void insert(){
		Session session = HibernateSessionFactory.getSession();
		Transaction tx = session.beginTransaction();
		Article art = new Article();
		art.setTitle("AAA");
		art.setVisitAmount(0);
		session.saveOrUpdate(art);
		tx.commit();
	}
	
	private  static void update(){
		Session session = HibernateSessionFactory.getSession();
		Transaction tx =  session.beginTransaction();
		Article art = (Article) session.load(Article.class, 1,LockMode.UPGRADE);
		art.setVisitAmount(art.getVisitAmount()+1);
		session.save(art);
		tx.commit();
		session.evict(art);
		
	}
	
	private static void work(){
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+":"+(i+1)+"times.");
			update();
		}
	}
	
	public static void main(String[] args) throws Exception{
		Thread t1 = new Thread(
			new Runnable(){
				public void run(){
					work();
				}
			}
		);
		
		Thread t2 = new Thread(
				new Runnable(){
					public void run(){
						work();
					}
				}
		);
		t1.setName("Thread1");
		t2.setName("Thread2");
		t1.setDaemon(true);
		t2.setDaemon(true);
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		
	}
}




查了一下文档,LockMode.UPGRADE是最高的锁级别了,如果换用其他的级别呢?我测试了LocMode.READ,发现还是不正确,用LockMode.WRITE的时候竟然抛异常。。大侠们谈谈看法吧:-)
   发表时间:2008-05-22  
ictboy 写道


PS:我在代码中打印出session的HashCode,发现每次的都不一样,这说明HibernateCallback每次都会创建新的session,至少在默认情况下是这样的。


没有使用Spring来管理事务导致的......
0 请登录后投票
   发表时间:2008-05-22  
最近刚刚看过Spring的事务管理,有点收获。建议也看一下吧。
另外呢,Hibernate针对并发有两种模型,乐观锁和悲观锁,可以看看它的文档。
针对不同的用况可以选择不同的解决方案,所以不是说“没有使用Spring来管理事务导致的......”。
0 请登录后投票
   发表时间:2008-05-22  
悲观锁性能下降比较大
0 请登录后投票
   发表时间:2008-05-24  
这个场景还是用悲观锁比较适合
0 请登录后投票
   发表时间:2008-05-28  
对于web应用程序运用悲观锁之后,何时释放锁怎么释放锁是个难题。
0 请登录后投票
   发表时间:2008-05-30  
这个地方为什么不能使用乐观锁,我觉得也是可以的。
0 请登录后投票
   发表时间:2008-05-30  
wang19841229 写道
这个地方为什么不能使用乐观锁,我觉得也是可以的。

你可以重复我的实验,看看用乐观锁行不行
0 请登录后投票
   发表时间:2008-06-04  
请问当Lock为Write时抛出什么异常?
0 请登录后投票
   发表时间:2008-06-04  
所谓的LockMode.UPGRADE就是oracle里面的select .. for udpate。

可能你的数据库不支持读锁定。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics