java与concurrenthashmap同步失败

问题描述 投票:0回答:1

我需要生成一个唯一的编号,目前的逻辑是:

有一个表

tableA
,其中包括列
id
(pk),
code
(varchar,unique)和
codeValue
(int)。

生成唯一编号时,系统会通过

codeValue
得到
code
, 如果没有记录,系统将在该表中插入一条
codeValue
为1的记录,并以
xxx00001
作为唯一编号; 如果有记录,系统将把
codeValue
+1作为唯一编号,如
xxx00002
,并将表中的
codeValue
更新为2。

java调用如下(srping boot 2.x):

@Service
class AServiceImpl extends BaseImpl {

public String getNewNo(String p, Integer b, String d) {// `code` of `tableA` is composed of p, b and d.

        StringBuffer sb = new StringBuffer();

        synchronized(getNoType(b, p, d)) {
            
            BeanA beanA = query record from `tableA` by `code` 
            Integer s = null;
            if (beanA.getId() == null) {
                s = 1;
                beanA.setCodeValue(1);
                insert beanA to `tableA`;
            } else {
                s = 1 + beanA.getCodeValue();// beanA.getCodeValue() return Integer
                beanA.setCodeValue(s);
                update beanA;
            }

            sb.append(beanA.getCode()); 
            
            sb.append(String.format("%04d", s));// 1 -> 0001, 1234 -> 1234
        }       

        return sb.toString();
    }
    
}

AServiceImpl
延伸
BaseImpl
并且
getNoType
位于
BaseImpl
中,如下所示:

@Service
public class BaseImpl {

private static Map<String, Object> noType = new ConcurrentHashMap<>();

public Object getNoType(Integer b, String p, String d) {        
        
        StringBuffer sb = new StringBuffer();
        if( b != null ) sb.append(b);
        if( !StringUtil.isEmptyString(p) ) sb.append(p);
        if( !StringUtil.isEmptyString(d) ) sb.append(d);        
        String type = sb.toString();
        
        noType.putIfAbsent(type, new Object());
        
        return noType.get(type);
    }
    
}

我想要的是,对于相同的代码,即使存在并发条件,

getNewNo
也总是返回不同的唯一数字,所以我尝试使用
synchronized
ConcurrentHashMap
来实现这个目标。

但是,有时当有两个人同时执行操作时,

getNewNo
会返回相同的数字(对于相同的
code
),例如有两个相同的数字
xxxx0488
。(插入/更新数据库时没有错误)

可能出现哪些问题?谢谢!

java spring concurrency
1个回答
0
投票

您可以引入锁存储并以原子方式从那里获取锁。 尝试这样的事情

@Service
public class AServiceImpl extends BaseImpl {

private final Map<NoType, Lock> lockMap = new ConcurrentHashMap<>();

record NoType(String p, Integer b, String d) {
}

public String getNewNo(String p, Integer b, String d) {
    StringBuffer sb = new StringBuffer();
    var noType = new NoType(p, b, d);
    lockMap.putIfAbsent(noType, new ReentrantLock());
    Lock lock = lockMap.get(noType);
    lock.lock();
    try {
        BeanA beanA = query record from `tableA`by `code`
        Integer s = null;
        if (beanA.getId() == null) {
            s = 1;
            beanA.setCodeValue(1);
            insert beanA to `tableA`;
        } else {
            s = 1 + beanA.getCodeValue();// beanA.getCodeValue() return Integer
            beanA.setCodeValue(s);
            update beanA;
        }

        sb.append(beanA.getCode());
        sb.append(String.format("%04d", s));// 1 -> 0001, 1234 -> 1234
        return sb.toString();
    } finally {
        lock.unlock();
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.