简易序列号实现

  1 package com.itch;
  2 import java.text.SimpleDateFormat;
  3 import java.util.Calendar;
  4 import java.util.Date;
  5 import java.util.concurrent.atomic.AtomicInteger;
  6 import java.util.concurrent.atomic.AtomicReference;
  7 
  8 /**
  9  * <p> 本类可以简易生成唯一序列号,线程安全<b>【待验证】</b>
 10  * <p> 序列号可由yyyyMMddHHmmss + [machineId(机器id) + tid(运行线程id)] + index(序号) 等部分组成
 11  * <p> 实现思路:以当前运行时间,每秒钟先按序分配(从下标0开始)。若当前秒不足以分配,则向未来秒数进行预分配
 12  * <hr>
 13  * <em> 仅供学习参考,欢迎大家一起测试,争取能用到生产环境。lol..
 14  * @author tianchong_01011@163.com
 15  * @version 0.1
 16  */
 17 public final class SimpleUniqueNo {
 18     
 19     /**
 20      *     默认每秒可分配序号位数
 21      */
 22     public static final int DEFAULT_INDEX_BIT_PER_SECOND = 4;
 23     
 24     /**
 25      *     默认machine id
 26      */
 27     public static final String DEFAULT_MACHINE_ID = "00";
 28     
 29     /**
 30      *     计数器
 31      */
 32     private volatile AtomicInteger indexCounter;
 33     
 34     /**
 35      *     上一次执行时间
 36      */
 37     private volatile Date lastestTime;
 38     
 39     /**
 40      *     超过每秒最大分配时的基准时间
 41      */
 42     private AtomicReference<Date> overBaseTime;
 43     
 44     /**
 45      *     日期格式化对象 
 46      */
 47     private ThreadLocal<SimpleDateFormat> threadLocal;
 48     
 49     /**
 50      *     预分配最大秒数,主要是为了空闲时重置 {@link #indexCounter},防止溢出
 51      */
 52     private volatile int maxOffset;
 53     
 54     /**
 55      *  int 溢出次数
 56      */
 57     private volatile int intOverflowTimes;
 58     
 59     /**
 60      *     每秒可分配序号位数
 61      */
 62     private final int indexBitPerSecond;
 63     
 64     private final int maxIndexNum;
 65     
 66     /**
 67      *     每秒请求数
 68      */
 69     private volatile AtomicInteger threadNumPerSecond;
 70     
 71     /**
 72      *     机器编号
 73      */
 74     private final String machineId;
 75     
 76     public SimpleUniqueNo() {
 77         this(DEFAULT_INDEX_BIT_PER_SECOND);
 78     }
 79     
 80     /**
 81      *     
 82      * @param indexBitPerSecond 每秒分配位数,由调用者保证位数不会造成interger溢出
 83      */
 84     public SimpleUniqueNo(int indexBitPerSecond) {
 85         this(indexBitPerSecond, DEFAULT_MACHINE_ID);
 86     }
 87     
 88     /**
 89      * 
 90      * @param indexBitPerSecond 每秒分配位数,由调用者保证位数不会造成interger溢出
 91      * @param machineNo    机器号
 92      */
 93     public SimpleUniqueNo(int indexBitPerSecond, String machineId) {
 94         super();
 95         this.indexCounter = new AtomicInteger();
 96         this.threadLocal = new ThreadLocal<>();
 97         this.lastestTime = new Date();
 98         this.overBaseTime = new AtomicReference<>();
 99         this.indexBitPerSecond = indexBitPerSecond;
100         this.machineId = machineId;
101         StringBuilder builder = new StringBuilder("1");
102         for (int i=0; i<indexBitPerSecond; i++) {
103             builder.append("0");
104         }
105         this.maxIndexNum = Integer.parseInt(builder.toString());
106         this.threadNumPerSecond = new AtomicInteger();
107     }
108     
109     public String genNo() {
110         SimpleDateFormat sdf = threadLocal.get();
111         if (null == sdf) {
112             sdf = new SimpleDateFormat("yyyyMMddHHmmss");
113             threadLocal.set(sdf);
114         }
115         
116         Date date = new Date();
117         Calendar cd = Calendar.getInstance();
118         cd.setTime(date);
119         String ns = "" + cd.get(Calendar.YEAR) + cd.get(Calendar.MONTH) + cd.get(Calendar.DAY_OF_MONTH) 
120                 + cd.get(Calendar.HOUR_OF_DAY) + cd.get(Calendar.MINUTE) + cd.get(Calendar.SECOND);
121         cd.setTime(lastestTime);
122         String os = "" + cd.get(Calendar.YEAR) + cd.get(Calendar.MONTH) + cd.get(Calendar.DAY_OF_MONTH) 
123                 + cd.get(Calendar.HOUR_OF_DAY) + cd.get(Calendar.MINUTE) + cd.get(Calendar.SECOND);
124         
125         int i = indexCounter.get();
126         if (i < maxIndexNum && ns.hashCode() != os.hashCode() && lastestTime.before(date)) {    // 新的1s 重置计数  
127             synchronized (this) {
128                 cd.setTime(lastestTime);
129                 os = "" + cd.get(Calendar.YEAR) + cd.get(Calendar.MONTH) + cd.get(Calendar.DAY_OF_MONTH) 
130                         + cd.get(Calendar.HOUR_OF_DAY) + cd.get(Calendar.MINUTE) + cd.get(Calendar.SECOND);
131                 i = indexCounter.get();
132                 if (i < maxIndexNum && ns.hashCode() != os.hashCode() && lastestTime.before(date)) {        // 防止更新过期时间
133                     Date oldLastestTime = lastestTime;
134                     while (threadNumPerSecond.get() > 0) {
135                         Thread.yield();
136                     }
137                     i = indexCounter.get();
138                     if ((intOverflowTimes == 0 && i < maxIndexNum)) {    // 非溢出借位 重置  
139                         indexCounter.set(0);
140                     } else {    // idle reset avoid integer overflow
141                         long offset = date.getTime() - oldLastestTime.getTime();
142                         if (offset / 1000 > maxOffset) {
143                             indexCounter.set(0);
144                             intOverflowTimes = 0;
145                             maxOffset = 0;
146                             overBaseTime.set(null);
147                         }
148                     }
149                     
150                     lastestTime = date;
151                 }
152             }
153         }
154         
155         threadNumPerSecond.getAndIncrement();
156         int c = indexCounter.getAndIncrement();
157         threadNumPerSecond.getAndDecrement();
158         if (c < 0) {    // integer overflow
159             c = indexCounter.get();
160             if (indexCounter.compareAndSet(c, 0)) {
161                 intOverflowTimes++;
162             } else {
163                 while (indexCounter.get() < 0) {    // waitting
164                     // do nothing
165                 }
166             }
167             
168             c = indexCounter.getAndIncrement();
169         }
170 
171         if (intOverflowTimes > 0 || c >= maxIndexNum) {    // 溢出借未来时间
172             date = overBaseTime.get() != null ? overBaseTime.get() : date;
173             cd.setTime(date);
174             overBaseTime.compareAndSet(null, date);
175             int offset = (c / maxIndexNum) + (Integer.MAX_VALUE / maxIndexNum) * intOverflowTimes;
176             cd.add(Calendar.SECOND, offset);
177             date = cd.getTime();
178             
179             maxOffset = Math.max(maxOffset, offset);
180             c = c % maxIndexNum;
181         }
182         
183         String time = null, mid = null, tid = null, index = null;
184         time = sdf.format(date);
185         mid = machineId;
186 //        tid = "" + Thread.currentThread().getId();
187         index = String.format("%0" + indexBitPerSecond + "d", c);
188         return time + mid + index;
189     }
190     
191     public static void main(String[] args) {
192         // 单机A系统
193         SimpleUniqueNo unique = new SimpleUniqueNo();
194         System.out.println(unique.genNo());
195         // 单机其他系统
196 //        SimpleUniqueNo unique = new SimpleUniqueNo();
197 //        System.out.println(unique.genNo());
198         
199         // 多机
200         SimpleUniqueNo u1 = new SimpleUniqueNo(DEFAULT_INDEX_BIT_PER_SECOND, "01");
201         SimpleUniqueNo u2 = new SimpleUniqueNo(DEFAULT_INDEX_BIT_PER_SECOND, "02");
202         System.out.println(u1.genNo());
203         System.out.println(u2.genNo());
204     }
205 
206 }

 

posted @ 2020-06-21 15:57  呆小田  阅读(279)  评论(0编辑  收藏  举报