Java对象全局锁处理ID自增重复问题:并发操作导致ID重复,单点应用程序或者分布式程序都需要加全局锁进行处理。数据库分库分表设计后,如果有全局唯一主表而每个子表业务又涉及到主表的新增,那么ID重复在多线程情况下很难避免,必须考虑全局唯一锁来处理这类数据的新增操作。
单点应用程序全局锁
单点应用,即只有一个Tomcat部署情况。如果只有一处代码做自增ID只需要加一个同步代码块就可以解决了,如果是多个地方都有相同的自增操作则需要加一个全局锁。
设置的全局锁对象
这里用的是net.sf.json.JSONObject,当然可以直接用Object。
public static JSONObject GLOBAL_OBJECT_LOCK= new JSONObject();
static{
// 设置全局对象锁的状态
GLOBAL_OBJECT_LOCK.put("unlock", true);
}
单个全局记录新增
// 同步等待机制:不建议一直阻塞接口,设置一个超时等待
long start=System.currentTimeMillis();
while (!XHRYCtrl.GLOBAL_OBJECT_LOCK.getBoolean("unlock")) {
try {
Thread.sleep(50);// 等待其他线程完成操作
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
// 最长等待5s
if (end - start > 5000) {
map.put("status", "0");
map.put("msg", "系统繁忙,请稍后再试");
return map;
}
}
// 代码同步块: 防止并发情况下,用户查询时出现读取和实际不一致情况====数据基础框架并发自增ID重复问题
synchronized (XHRYCtrl.GLOBAL_OBJECT_LOCK) {
XHRYCtrl.GLOBAL_OBJECT_LOCK.put("unlock", false);
try {
list = service.getEntityList("xh_hly_tb_zb", q);
} catch (ServiceException e1) {
e1.printStackTrace();
map.put("status", "0");
map.put("msg", "查询巡护员主表异常");
XHRYCtrl.GLOBAL_OBJECT_LOCK.put("unlock", true);
return map;
}
if(list.size()>0){
// 查询出用户单位机构
String dwcode=(String) list.get(0).getByFieldName("DW_CODE");
String nsjgId=(String) list.get(0).getByFieldName("NSJGID");
String descStr=getDwNsjgNames(dwcode,nsjgId);
map.put("status", "1");
map.put("msg", "用户手机号重复");
map.put("descStr",descStr);
XHRYCtrl.GLOBAL_OBJECT_LOCK.put("unlock", true);
return map;
}
try {
//先保存到总表
workSpace.StartEditing();
RowBase row=service.save("xh_hly_tb_zb", hlyRow);
hlyRow.getCurrentObjects().put("NSJGID", nsjgid);
hlyRow.getCurrentObjects().put("HLY_ID", row.getCurrentObjects().get("HLY_ID").toString());
//保存到分表
service.save("xh_hly_tb", hlyRow);
} catch (ServiceException e) {
e.printStackTrace();
workSpace.StopEditing(false);
map.put("status", "0");
map.put("msg", "添加失败");
XHRYCtrl.GLOBAL_OBJECT_LOCK.put("unlock", true);
return map;
}
XHRYCtrl.GLOBAL_OBJECT_LOCK.put("unlock", true);
}
获取锁来处理批量操作
boolean getLock=false;
try{
// 同步等待机制:不建议一直阻塞接口,设置一个超时等待
long start=System.currentTimeMillis();
while(!XHRYCtrl.GLOBAL_OBJECT_LOCK.getBoolean("unlock")){
try {
Thread.sleep(50);// 等待其他线程完成操作
} catch (InterruptedException e) {
e.printStackTrace();
}
long end=System.currentTimeMillis();
// 最长等待5s
if(end-start>5000){
throw new Exception("其他线程占用全局锁,等待超时!");
}
}
// 代码同步块: 防止并发情况下,用户查询时出现读取和实际不一致情况====数据基础框架并发自增ID重复问题
synchronized (XHRYCtrl.GLOBAL_OBJECT_LOCK){
//获取到锁
getLock=true;
XHRYCtrl.GLOBAL_OBJECT_LOCK.put("unlock",false);
for (int rowNum = startRow; rowNum <= maxRow; rowNum++) {
HSSFRow hssfRow = hssfSheet.getRow(rowNum);
// 保存行记录
saveRecordLine(rowNum, tableName, NSJGID, msgs, hssfRow, columns, successFields,limitUserCount, failedRows, successRows, idMapping, result,state);
}
}
} catch (JsonProcessingException e) {
result.put("success", Boolean.valueOf(false));
msgs.add("配置文件存在错误");
e.printStackTrace();
} catch (JsonParseException e) {
result.put("success", Boolean.valueOf(false));
msgs.add("配置文件存在错误");
e.printStackTrace();
} catch (IOException e) {
result.put("success", Boolean.valueOf(false));
msgs.add("导入的Excel文件存在错误");
e.printStackTrace();
} catch (Exception e) {
result.put("success", Boolean.valueOf(false));
msgs.add("系统繁忙,请稍后再试");
e.printStackTrace();
}finally{
if(getLock){
XHRYCtrl.GLOBAL_OBJECT_LOCK.put("unlock",true);
}
}
分布式部署全局锁
分布式部署,即通过Nginx负载均衡同一套应用部署在不同的服务器上提供相同的业务服务能力。对于全局唯一的值,需要用到分布式全局锁。
设计一个基本的分布式全局锁可以考虑使用Redis来做,将公共的数据放入redis缓存起来,。
待续.............