相关代码片段如下,加锁后,会从数据库中取记录(记录中有计数器),修改计数器,唯一标识根据该计数器生成
public class SerialnumUtil { ... private static final Map<String, ReentrantLock> locks = new ConcurrentHashMap(); @PostConstruct public void init() { // init locks ... } public String generate(String sdtype) { Lock lock = locks.get(sdtype); try { if (lock.tryLock(5, TimeUnit.SECONDS)) { ... return XXX; } else { throw new CustomMessageException("加锁失败"); } } catch (Exception ex) { log.error(ex.getMessage(), ex); throw new CustomMessageException("创建CODE失败"); } finally { if (null != lock) { lock.unlock(); } } } }
QUARTZ 任务调度方法中,会使用上述中 generate方法,生成一个标识符,在测试中发现多次调用会返回同一个标识符
1、将方法注释为独立事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
由于方法被事务方法调用,而该方法执行时间较长,如不将 generate 方法标注为新事务,容易在事务执行中,与另一事务运行中的事务同时调用 generate 方法,获取同一下记录,同时修改,最后生成同一个标识符
2、在 generate 方法中,在修改前再次查询记录,比较两记录修改时间是否一致,如不一致,等待,再次尝试
3、关闭 mybatis 缓存机制
<select id="XXX" resultMap="XXX" flushCache="true"> ??? </select>
参考资料: