锁定老帖子 主题:一个循环流水号实现,求评
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (4)
|
|
---|---|
作者 | 正文 |
发表时间:2011-09-19
weiqiang.yang 写道 调用参数里面还得传一个dao,这有点怪异吧
getNextSerialNumber不一定非得是static的 可以每次取下一个序列的时候都检查一下有没有初始化过,如果没有初始化,那么先从数据库读取数据进行初始化 如果已经初始化了,那么直接返回(内存中保存的数据+1)%LIMIT private static volatile boolean INITED = false; private static volatile int SN = 0; private void init(){ SN = xxxDao.xx(); // 从数据库加载最后一次持久化的值 INITED = true; } public synchronized String getNextSerialNumber(){ if(!INITED) init(); SN = (SN + 1) % LIMIT; // TODO: 更新数据库 return String.format("%06d", SN); } 这个思路比楼主的思路好。但个人觉得还有改进的地方。 每次获取就更新一次数据库可改进: 一次读取时,设置一个区间,如20,更新数据库的序号就是当前值+区间的值,而每次获取下一个序列号值则多一个校验,是否已经达到(当前值+区间)值,如达到,则重新往数据库读即可。 |
|
返回顶楼 | |
发表时间:2011-09-19
weiqiang.yang 写道 调用参数里面还得传一个dao,这有点怪异吧
getNextSerialNumber不一定非得是static的 可以每次取下一个序列的时候都检查一下有没有初始化过,如果没有初始化,那么先从数据库读取数据进行初始化 如果已经初始化了,那么直接返回(内存中保存的数据+1)%LIMIT private static volatile boolean INITED = false; private static volatile int SN = 0; private void init(){ SN = xxxDao.xx(); // 从数据库加载最后一次持久化的值 INITED = true; } public synchronized String getNextSerialNumber(){ if(!INITED) init(); SN = (SN + 1) % LIMIT; // TODO: 更新数据库 return String.format("%06d", SN); } 可以预见的是大并发下,getNextSerialNumber 会成为系统的一个瓶颈. |
|
返回顶楼 | |
发表时间:2011-09-19
直接用sequence好了,这么麻烦
|
|
返回顶楼 | |
发表时间:2011-09-19
我在想能不能缩小同步的范围,像这样:
public String getNextSerialNumber(){ int n = nextSerialNumber(); updateDatabase(n); } public synchronized String nextSerialNumber(){} public String updateDatabase(int n){} |
|
返回顶楼 | |
发表时间:2011-09-19
chunquedong 写道 我在想能不能缩小同步的范围,像这样:
public String getNextSerialNumber(){ int n = nextSerialNumber(); updateDatabase(n); } public synchronized String nextSerialNumber(){} public String updateDatabase(int n){} 会好些,但是换汤不换药.synchronized 的问题在于无论是不是并发都要上锁.所以应该用 AtomicLong 这些工具,这些工具实际调用CPU指令基于CAS,效率会更好. 这个问题有种情况,很让人挠头,就是要求系统重启后必须继续分配编号.这等于要求 nextSerialNumber 和 updateDatabase必须是原子性的.这种要求下,上面所有的代码都要完蛋.也就只能用数据库的sequence了.可是性能情况未知. |
|
返回顶楼 | |
发表时间:2011-09-20
util.concurrent 中的原子计数器
|
|
返回顶楼 | |
发表时间:2011-09-20
之前做过这样的设计。。还是需要持久化一下的哈。。
|
|
返回顶楼 | |
发表时间:2011-09-20
JE帐号 写道 weiqiang.yang 写道 调用参数里面还得传一个dao,这有点怪异吧
getNextSerialNumber不一定非得是static的 可以每次取下一个序列的时候都检查一下有没有初始化过,如果没有初始化,那么先从数据库读取数据进行初始化 如果已经初始化了,那么直接返回(内存中保存的数据+1)%LIMIT private static volatile boolean INITED = false; private static volatile int SN = 0; private void init(){ SN = xxxDao.xx(); // 从数据库加载最后一次持久化的值 INITED = true; } public synchronized String getNextSerialNumber(){ if(!INITED) init(); SN = (SN + 1) % LIMIT; // TODO: 更新数据库 return String.format("%06d", SN); } 可以预见的是大并发下,getNextSerialNumber 会成为系统的一个瓶颈. 不了解lz的并发是多大的并发,如果并发并不是很大,那我觉得getNext...方法加同步是没有问题的,并不是在方法上加同步就一定有瓶颈么 ~ 在方法上面加同步,是因为需要恢复重启前的最后一个序列 所以“计算下一个序列”和“持久化到数据库”需要保持原子性 如果能接受流水号不连续的话,那可以考虑楼上有人说到的,先将数据库的值更新为current + N,然后在接下来的N次调用都不更新数据库,用完N个序列之后再将数据库更新为current+N ... |
|
返回顶楼 | |
发表时间:2011-09-21
不赞同吧方法同步,太消耗性能,你可以试试那些并发的数据结构 ConcurrentHashMap,详见java.util.concurrent。
|
|
返回顶楼 | |
发表时间:2011-09-21
oracle里面有序列号一说。。。感觉实现这个问题就易如反掌了
|
|
返回顶楼 | |