一、介绍
在调度平台中,下发任务的单位有三种,分别是mission,job,task,因此我们在代码中也必须生成相应格式的missionId,jobId以及taskId。
对于代码生成id,我们使用的是+1的方式来进行生成,也就是说,假如现在的jobId为xxxx111,那么我们在生成下一个job时,赋予其的jobId应该为xxxx112,这部分应该很好理解,我们第一反应应该就是直接jobId++或++jobId。
二、问题
但实际上在Java语言中,++i和i++并不是线程安全的,我们来考虑下面这种情况:
假设现在的线程A、B、C同时在生成jobId,现在的jobId为xxxx111,A,B线程同时查询了jobId,在此基础上生成了新的job的id值,此时便会出现两个xxxx112。导致jobId丢失唯一性。由此,我们产生了下面这个问题。
问题:如何在多线程的情况下保证生成Id不会重复?
三、预备知识:AtomicInteger,一个提供原子操作的Integer的类。使用该类能保证线程安全,其使用方法如下:
//获取当前的值
public final int get()
//取当前的值,并设置新的值
public final int getAndSet(int newValue)
//获取当前的值,并自增
public final int getAndIncrement()
//获取当前的值,并自减
public final int getAndDecrement()
//获取当前的值,并加上预期的值
public final int getAndAdd(int delta)
四、解决方案:
1、使用单例模式。(spring注入自带单例模式)
需要注意的是,spring中的单例模式是指:对于每一个IOC容器,它只会生成唯一的实例
也就是说,我们必须只生成一个applicationContext,然后所有操作都在这个applicationContext下。
详见我的上一篇博客“新调度平台-5”
2、使用ConcurrentHashMap
关键代码如下截图所示:
五、具体代码:
1、生成missionId的MissionMapper:
missionId=userId + missionCount(6位)组成
package com.YYSchedule.task.mapper;
import java.text.DecimalFormat;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.YYSchedule.common.mybatis.pojo.UserBasic;
import com.YYSchedule.store.service.UserBasicService;
@Component("MissionMapper")
@Scope("singleton")
public class MissionMapper
{
@Autowired
private UserBasicService userBasicService;
/**
* Map<UserId, MissionCount>
*/
private Map<Integer, AtomicInteger> missionCountMap = new ConcurrentHashMap<Integer, AtomicInteger>();
private MissionMapper(){
}
public synchronized Map<Integer, AtomicInteger> getMissionCountMap()
{
return missionCountMap;
}
/**
* init mission count map
* @param missionType
* @param missionCount
*/
public synchronized void initMissionCountMap(Integer userId, Integer missionCount) {
if (missionCountMap.get(userId) == null) {
missionCountMap.put(userId, new AtomicInteger(missionCount));
}
}
/**
* get missionCount
*
* @param userId
* @return
*/
public synchronized int getMissionCount(Integer userId)
{
if(missionCountMap.get(userId) != null)
{
return missionCountMap.get(userId).get();
}
else
{
return 0;
}
}
/**
* increase and get mission count
*
* @param userId
* @return
*/
public synchronized int increaseAndGetMissionCount(Integer userId)
{
int missionCount = missionCountMap.get(userId).incrementAndGet();
// update mission count in database
UserBasic userBasic = new UserBasic();
userBasic.setUserId(userId);
userBasic.setMissionCount(missionCount);
System.out.println(missionCount);
userBasicService.updateUserBasic(userBasic);
return missionCount;
}
/**
* generate new mission id
*
* @param
* @return
*/
public synchronized long generateMissionId(Integer userId) {
StringBuilder sbuilder = new StringBuilder();
sbuilder.append(userId);
int missionCount = increaseAndGetMissionCount(userId);
sbuilder.append(new DecimalFormat("0000000").format(missionCount));
return Long.parseLong(sbuilder.toString());
}
}
2、生成jobId的JobMapper:
jobId=missionId + jobCount(2位)组成
package com.YYSchedule.task.mapper;
import java.text.DecimalFormat;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.YYSchedule.common.mybatis.pojo.MissionBasic;
import com.YYSchedule.store.service.MissionBasicService;
@Component("JobMapper")
@Scope("singleton")
public class JobMapper
{
@Autowired
private MissionBasicService missionBasicService;
/**
* Map<MissionId, JobCount>
*/
private Map<Integer, AtomicInteger> jobCountMap = new ConcurrentHashMap<Integer, AtomicInteger>();
private JobMapper(){
}
public synchronized Map<Integer, AtomicInteger> getJobCountMap()
{
return jobCountMap;
}
/**
* init job count map
* @param jobType
* @param jobCount
*/
public synchronized void initJobCountMap(Integer missionId, Integer jobCount) {
if (jobCountMap.get(missionId) == null) {
jobCountMap.put(missionId, new AtomicInteger(jobCount));
}
}
/**
* get jobCount
*
* @param missionId
* @return
*/
public synchronized int getJobCount(Integer missionId)
{
if(jobCountMap.get(missionId) != null)
{
return jobCountMap.get(missionId).get();
}
else
{
return 0;
}
}
/**
* increase and get job count
*
* @param missionId
* @return
*/
public synchronized int increaseAndGetJobCount(Integer missionId)
{
int jobCount = jobCountMap.get(missionId).incrementAndGet();
// update job count in database
MissionBasic missionBasic = new MissionBasic();
missionBasic.setMissionId(missionId);
missionBasic.setJobCount(jobCount);
missionBasicService.updateMissionBasic(missionBasic);
return jobCount;
}
/**
* generate new job id
*
* @param
* @return
*/
public synchronized int generateJobId(Integer missionId) {
StringBuilder sbuilder = new StringBuilder();
sbuilder.append(missionId);
int jobCount = increaseAndGetJobCount(missionId);
sbuilder.append(new DecimalFormat("00000").format(jobCount));
return Integer.parseInt(sbuilder.toString());
}
}