根据城市+分类从百度地图中获取对应的商户信息(名称、地址、经纬度、图片、评论等)
百度地图提供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; }