`
lijiejava
  • 浏览: 262366 次
  • 性别: Icon_minigender_1
  • 来自: 南通
社区版块
存档分类
最新评论

数据库--悲观锁

 
阅读更多
项目需求:
根据城市+分类从百度地图中获取对应的商户信息(名称、地址、经纬度、图片、评论等)

百度地图提供API,可以根据城市+分类获取对应的商户,如以下url查询苏州地区的火锅类商户:

http://api.map.baidu.com/place/v2/search?&query=%E7%81%AB%E9%94%85&region=%E8%8B%8F%E5%B7%9E&output=json&scope=2&ak=887a60037f9b2c3001a3dbd2a80c591e&page_size=20&page_num=1

以获取江苏地区的火锅类为例:

新建一张表: t_search_record (id, cityName, categoryName, status)

status  0 – 待查询   1 - 进行中   2 – 已完成

江苏省下面有13个城市   t_search_record 表:


     南京     火锅      0
     苏州      火锅       0
     无锡      火锅       0
     常州      火锅       0
     扬州      火锅       0
     南通      火锅       0
     连云港      火锅       0
     徐州      火锅       0
     盐城      火锅       0
     淮安      火锅       0
     泰州      火锅       0
     镇江      火锅       0
     宿迁      火锅       0


ExecutorService threadPool = Executors.newFixedThreadPool(Constants.THREAD_AMOUNT);
for(int i = 0; i < Constants.THREAD_AMOUNT; i++) { 
     threadPool.execute(new Runnable() { 
          public void run() { 
               // search data
               searchData();
          } 
     }); 
}
threadPool.shutdown();


searchData方法查询百度商户数据 

步骤如下:
1. 获取t_search_record表中一条待执行的记录(status=0)
2. 根据记录中的cityName、categoryName查询数据并存入相关表中

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

数据库t_task表:




开启3个线程去获取未执行的任务,获取任务的代码:

Service中的getTask方法,获取未执行的task,然后修改status

public Task getTask(){   // 获取status=0的Task
		Task task =  lockDao.getTask();
		if(task!=null){
			updateStatus(task.getId());
		}
		return task;
	}

StringBuilder sb = new StringBuilder(" from Task t where t.status = :status");   
Query query = entityManager.createQuery(sb.toString(), Task.class);  
query.setParameter("status", 0);  
List<Task> result = query.getResultList(); 
if(result!=null && result.size()>0){
	return result.get(0); 
}
return null;  



输出结果:
任务名称:任务1;任务状态:0
任务名称:任务1;任务状态:0
任务名称:任务1;任务状态:0


可见3个线程获取的是同一个任务,这样就会导致重复执行任务。


采用数据库锁

在查询任务时采用悲观锁:

StringBuilder sb = new StringBuilder(" from Task t where t.status = :status");   
Query query = entityManager.createQuery(sb.toString(), Task.class)
		                .setLockMode(LockModeType.PESSIMISTIC_WRITE);
query.setParameter("status", 0);  
List<Task> result = query.getResultList(); 
if(result!=null && result.size()>0){
   return result.get(0); 
}
return null;


输出结果为:
任务名称:任务1;任务状态:0
任务名称:任务2;任务状态:0
任务名称:任务3;任务状态:0

任务名称:任务1;任务状态:0
任务名称:任务3;任务状态:0
任务名称:任务2;任务状态:0

没有出现重复! 

悲观写


改成悲观读机制:

第1个线程输出: 任务名称:任务1;任务状态:0
后面2个线程 出现死锁异常:
org.springframework.dao.CannotAcquireLockException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.LockAcquisitionException: could not execute statement
………
Deadlock found when trying to get lock; try restarting transaction

原因:PESSIMISTIC_READ 表示 只要事务读取db,管理器就会锁定db数据,直到事务提交时才解锁,所以在进行update的会发生异常!

关于PESSIMISTIC_WRITE
query+update 必须在同一个事务之中......

PESSIMISTIC_WRITE  悲观锁
对应sql : 
select * from t_task for update;


当第一个线程查询时 调用query.getResultList()后,数据就被for update
这时其它的线程无法进行悲观锁查询  (由于这边锁住,即所有线程都是for update查询,所以其它线程都在等待,不会出现查询出同一条数据的问题)

在Mysql中输入:
select * from t_task for update; 
  显示在等待,没有查询结果(截图如下)  直到debug结束,才会出现查询结果





而如果输入 select * from t_task  则可以直接输出结果;行锁,查询其它记录Ok

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

如果多个线程同时更新一条记录,如总部账户数据,那么可能会出现如下异常:

Caused by: org.hibernate.exception.LockTimeoutException: could not extract ResultSet 
	......
Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction



	public Task getTask(){   // 获取status=0的Task
		 
		Task task =  taskDao.getTask();
		if(task!=null){ 
			// updateStatus(task.getId());
			
			// 在调用getTask方法时获取锁,直到事务提交才释放,如果在getTask之前sleep,还未获取锁,不会超时
			
//			try{
//				Thread.sleep(5000);
//			}catch(Exception e){
//				e.printStackTrace();
//			}
			
			updateTaskName(task.getId(), "xxxx");
		}
		// System.out.println(Thread.currentThread().getName()+"--service end");
		return task;
	}



分享到:
评论

相关推荐

    数据库事务、hibernate悲观锁和乐观锁

    在Hibernate中,可以通过设置`@LockModeType.PESSIMISTIC_READ`或`@LockModeType.PESSIMISTIC_WRITE`来实现悲观锁。悲观锁的优势在于防止其他事务修改数据,但缺点是可能导致锁竞争,降低并发性能。 乐观锁则相反,...

    数据库乐观锁与悲观锁1

    总的来说,数据库的锁机制是确保数据一致性的重要工具,理解并合理使用乐观锁和悲观锁,可以帮助我们设计出更高效、更稳定的数据库应用程序。在数据库设计时,应充分考虑并发控制策略,根据业务特点选择合适的锁类型...

    多用户远程访问Access数据库--通讯录示例(VB+ASP+ACCE)

    9. **并发控制**:为了处理多用户同时操作,系统可能采用了并发控制策略,如乐观锁或悲观锁,以防止数据冲突。 10. **Web发布和数据同步**:ASP可能用于处理Web请求,实现用户通过Web浏览器访问和编辑通讯录。考虑...

    Java 中的悲观锁和乐观锁的实现

    在Hibernate中,悲观锁的实现依赖于数据库的锁定机制。开发者可以通过在查询语句中添加锁定模式来实现悲观锁。例如: ```java String hqlStr = "FROM TUser AS user WHERE user.name = 'Erica'"; Query query = ...

    thinkPHP框架乐观锁和悲观锁实例分析

    在ThinkPHP框架中,常见的有乐观锁和悲观锁两种机制。本文将通过实例分析这两种锁在ThinkPHP框架中的应用,以及它们各自的优缺点。 首先,让我们从乐观锁开始了解。乐观锁机制假设多个事务在处理数据时很少发生冲突...

    Hibernate悲观锁和乐观锁的实现

    悲观锁和乐观锁是两种常见的锁定策略,它们各有特点,适用于不同的场景。 **悲观锁**(Pessimistic Lock)的名字来源于其悲观的态度,它认为数据随时可能被其他事务修改,因此在读取数据时就立即进行加锁,防止其他...

    MySQL悲观锁总结和实践

    悲观锁的主要实现方式依赖于数据库提供的锁机制,特别是事务隔离级别中的行级锁或表级锁。只有数据库层面提供的锁机制才能确保数据访问的排他性,避免多线程或多事务环境下的并发冲突。若仅在应用层面上实现加锁机制...

    sybase数据库查被锁的表.doc

    2. 使用乐观锁或悲观锁机制。 3. 使用 Row-Level locking 机制。 4. 避免使用长事务和大量的锁定对象。 5. 使用 Index 机制来提高查询效率。 Sybase 数据库锁机制和锁表查询是数据库管理系统中非常重要的组件,可以...

    面试必备之乐观锁与悲观锁.pdf

    传统的关系型数据库系统中,悲观锁的应用非常广泛,例如行级锁、表级锁、读锁和写锁等。在Java中,`synchronized`关键字和`ReentrantLock`类都是悲观锁的典型实现。 **特点**: - 数据访问前必须获得锁。 - 只有...

    共享锁,更新锁,乐观锁,悲观锁 和 数据库中锁的使用.doc

    共享锁,更新锁,乐观锁,悲观锁 和 数据库中锁的使用

    Hibernate的乐观锁与悲观锁

    悲观锁主要依赖于数据库层面提供的锁机制来实现。例如,可以通过`SELECT ... FOR UPDATE`语句来锁定符合条件的数据行,直到当前事务结束才会释放锁。 ##### 2.3 示例 假设我们需要更新账户名为“Erica”的用户的...

    乐观锁与悲观锁

    介绍数据库事务的定义和事务带来的问题,详细讲解乐观锁与悲观锁的区别

    面试必备之乐观锁与悲观锁.zip

    乐观锁和悲观锁是数据库事务控制中的两种基本策略,它们在多用户并发环境下处理数据时起着关键作用。理解这两种锁的概念以及它们的工作原理对于任何IT专业人士,尤其是开发者和DBA来说,都是至关重要的面试准备内容...

    Hibernate乐观锁和悲观锁分析

    【Hibernate乐观锁与悲观锁详解】 在开发过程中,尤其是在并发环境下,确保数据的一致性和完整性至关重要。Hibernate,作为Java领域广泛使用的ORM框架,提供了一种处理并发数据访问冲突的手段,那就是锁机制。主要...

    张红娟数据库原理实验-杭电-杭州电子科技大学

    6. **事务与并发控制**:学习事务的ACID(原子性、一致性、隔离性和持久性)属性,以及并发控制策略,如锁定机制和乐观锁/悲观锁的概念。 7. **备份与恢复**:理解数据库备份的重要性,学习如何执行完整备份、差异...

    数据库面试题--值得一看

    - 锁机制:行级锁、表级锁、页级锁,以及乐观锁和悲观锁的比较。 5. **数据库备份与恢复** - 备份类型:完整备份、增量备份、差异备份。 - 数据恢复策略:如何使用日志文件进行恢复,理解RMAN(Recovery Manager...

Global site tag (gtag.js) - Google Analytics