• 简易序列号实现


      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 }
  • 相关阅读:
    『水晶报表』实现打印
    C#水晶报表教程
    C#
    C# 跨线程调用控件
    SQL中DateTime转换成Varchar样式
    解决SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问的方法
    配置JAVA的环境变量
    lucene3.0_IndexSearcher排序
    tomcat如何配置环境变量
    无法启动DISTRIBUTED TRANSACTION COORDINATOR解决方法
  • 原文地址:https://www.cnblogs.com/daixiaotian/p/13172690.html
Copyright © 2020-2023  润新知