项目需求:
根据城市+分类从百度地图中获取对应的商户信息(名称、地址、经纬度、图片、评论等)
百度地图提供API,可以根据城市+分类获取对应的商户,如以下url查询苏州地区的火锅类商户:
http://api.map.baidu.com/place/v2/search?&query=%E7%81%AB%E9%94%85®ion=%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中,可以通过设置`@LockModeType.PESSIMISTIC_READ`或`@LockModeType.PESSIMISTIC_WRITE`来实现悲观锁。悲观锁的优势在于防止其他事务修改数据,但缺点是可能导致锁竞争,降低并发性能。 乐观锁则相反,...
总的来说,数据库的锁机制是确保数据一致性的重要工具,理解并合理使用乐观锁和悲观锁,可以帮助我们设计出更高效、更稳定的数据库应用程序。在数据库设计时,应充分考虑并发控制策略,根据业务特点选择合适的锁类型...
9. **并发控制**:为了处理多用户同时操作,系统可能采用了并发控制策略,如乐观锁或悲观锁,以防止数据冲突。 10. **Web发布和数据同步**:ASP可能用于处理Web请求,实现用户通过Web浏览器访问和编辑通讯录。考虑...
在Hibernate中,悲观锁的实现依赖于数据库的锁定机制。开发者可以通过在查询语句中添加锁定模式来实现悲观锁。例如: ```java String hqlStr = "FROM TUser AS user WHERE user.name = 'Erica'"; Query query = ...
在ThinkPHP框架中,常见的有乐观锁和悲观锁两种机制。本文将通过实例分析这两种锁在ThinkPHP框架中的应用,以及它们各自的优缺点。 首先,让我们从乐观锁开始了解。乐观锁机制假设多个事务在处理数据时很少发生冲突...
悲观锁和乐观锁是两种常见的锁定策略,它们各有特点,适用于不同的场景。 **悲观锁**(Pessimistic Lock)的名字来源于其悲观的态度,它认为数据随时可能被其他事务修改,因此在读取数据时就立即进行加锁,防止其他...
悲观锁的主要实现方式依赖于数据库提供的锁机制,特别是事务隔离级别中的行级锁或表级锁。只有数据库层面提供的锁机制才能确保数据访问的排他性,避免多线程或多事务环境下的并发冲突。若仅在应用层面上实现加锁机制...
2. 使用乐观锁或悲观锁机制。 3. 使用 Row-Level locking 机制。 4. 避免使用长事务和大量的锁定对象。 5. 使用 Index 机制来提高查询效率。 Sybase 数据库锁机制和锁表查询是数据库管理系统中非常重要的组件,可以...
传统的关系型数据库系统中,悲观锁的应用非常广泛,例如行级锁、表级锁、读锁和写锁等。在Java中,`synchronized`关键字和`ReentrantLock`类都是悲观锁的典型实现。 **特点**: - 数据访问前必须获得锁。 - 只有...
共享锁,更新锁,乐观锁,悲观锁 和 数据库中锁的使用
悲观锁主要依赖于数据库层面提供的锁机制来实现。例如,可以通过`SELECT ... FOR UPDATE`语句来锁定符合条件的数据行,直到当前事务结束才会释放锁。 ##### 2.3 示例 假设我们需要更新账户名为“Erica”的用户的...
介绍数据库事务的定义和事务带来的问题,详细讲解乐观锁与悲观锁的区别
乐观锁和悲观锁是数据库事务控制中的两种基本策略,它们在多用户并发环境下处理数据时起着关键作用。理解这两种锁的概念以及它们的工作原理对于任何IT专业人士,尤其是开发者和DBA来说,都是至关重要的面试准备内容...
【Hibernate乐观锁与悲观锁详解】 在开发过程中,尤其是在并发环境下,确保数据的一致性和完整性至关重要。Hibernate,作为Java领域广泛使用的ORM框架,提供了一种处理并发数据访问冲突的手段,那就是锁机制。主要...
6. **事务与并发控制**:学习事务的ACID(原子性、一致性、隔离性和持久性)属性,以及并发控制策略,如锁定机制和乐观锁/悲观锁的概念。 7. **备份与恢复**:理解数据库备份的重要性,学习如何执行完整备份、差异...
- 锁机制:行级锁、表级锁、页级锁,以及乐观锁和悲观锁的比较。 5. **数据库备份与恢复** - 备份类型:完整备份、增量备份、差异备份。 - 数据恢复策略:如何使用日志文件进行恢复,理解RMAN(Recovery Manager...