订单号生成逻辑,C#和JAVA双版

五年没写过博客了,倒是天天在看

转来转去,又转回技术

原来一直在使用微软爸爸的东西,最近一两年开始玩android,玩java,还有PostgreSQL

都有些应用了,倒是可以整理些随笔出来,这就是其中一篇吧

c#是java的优雅版本,java的linq就是一坨那啥,嗯!觉得不爽就别看了

订单号这个玩意我想有几点得保证

1,有顺序但不连续

2,不能太长,最好15位以内

3,最好全数字

目前来说谷歌和百度出来的感觉都达不到我要的这些要求,不是太长超过20位,就是会加上字母,再就是会连续,否则就没顺序,没有办法只有自己想

本来没想自己干的,这玩意绝对是个老问题,估计大家都有好办法,藏着了吧

开干,思路如下:

得有时间在里面,这样有顺序

不连续好办,加上随机尾数

不能太长也好办,取一部分时间戳,或者是当前时间减去过去一个固定时间的差值

OK,上代码,先JAVA

  1 import java.io.*;
  2 import java.util.UUID;
  3 import java.util.concurrent.atomic.AtomicLong;
  4 
  5 /**
  6  * Created by 黑曜石 on 2017/5/19.
  7  */
  8 public class TradeNoGenerator {
  9     //基准时间截 (2017-01-01)
 10     private static final long initStamp=1483200000000L;
 11     //每秒基准容量1000个
 12     private static final int secondsSpace=1000;
 13     //基准容量扩容倍数
 14     private static final int secondsSpaceMulti=10;
 15     //多少秒不用,重新设定原子初始值
 16     private static final int secondsRefresh=60;
 17     //原子初始值
 18     private AtomicLong atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti);
 19 
 20     private TradeNoGenerator() {
 21     }
 22 
 23     private static class TradeNoGeneratorHolder{
 24         private static TradeNoGenerator instance = new TradeNoGenerator();
 25     }
 26 
 27     public static TradeNoGenerator getInstance() {
 28         return TradeNoGeneratorHolder.instance;
 29     }
 30 
 31     //线程安全
 32     /**
 33      * 用这个,用这个
 34      * @return
 35      * 生成结果例:11983017246298
 36      * 由三段组成,11983017,2462,98
 37      * 第一段11983017,位数会随着时间增长增加,计算方式为与initStamp相加,结果为到秒的时间戳
 38      * 第二段,2462,看容量决定位数,容量=secondsSpace*secondsSpaceMulti,原子增长字段
 39      * 第三段98,固定2位,随机数
 40      */
 41     public synchronized String getNo(){
 42         long no=atomicLong.getAndIncrement();
 43         //当前秒时间戳与基准时间戳秒级数量
 44         long currentSecond = (System.currentTimeMillis()-initStamp) / 1000;
 45         //计数超过60秒未使用过,则重新给定初始值
 46         if (currentSecond-no/(secondsSpace*secondsSpaceMulti)>=secondsRefresh){
 47             //重置原子计数原始值
 48             atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti);
 49             no=atomicLong.getAndIncrement();
 50         }
 51         //计算容量是否超出
 52         if (no-currentSecond*secondsSpace*secondsSpaceMulti>=secondsSpace*secondsSpaceMulti){
 53             //超出则等待下一秒的到来,这里计算到下一整数秒的微秒差
 54             long millisWithinSecond = System.currentTimeMillis() % 1000;
 55             try {
 56                 Thread.sleep(1000 - millisWithinSecond);
 57             } catch (InterruptedException e) {
 58                 e.printStackTrace();
 59             }
 60 
 61             System.out.println(">>> new second . "+no +" "+Thread.currentThread().getName());
 62 
 63             //重置原子计数原始值
 64             atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti);
 65             no=atomicLong.getAndIncrement();
 66         }
 67 
 68         int hashCode= UUID.randomUUID().hashCode();
 69         hashCode=hashCode<0?-hashCode:hashCode;
 70         String randomStr=String.valueOf(hashCode).substring(0,2);
 71         return no+randomStr;
 72     }
 73 
 74     /**
 75      * 获取时间戳
 76      * @param tradeNo
 77      * @return
 78      */
 79     public long getTimestamp(String tradeNo){
 80         int spaceLen = String.valueOf(secondsSpace*secondsSpaceMulti).length()-1;
 81         int stampLen=tradeNo.length()-spaceLen-2;
 82         String stampStr=tradeNo.substring(0,stampLen);
 83         //右侧补齐微秒
 84         long stamp=Long.parseLong(stampStr+String.format("%1$03d",0));
 85         stamp+=initStamp;
 86         return stamp;
 87     }
 88 
 89     public static void main(String[] args) {
 90 
 91         File file=new File("e:/tradeno1.txt");
 92         if (file.exists()){
 93             file.delete();
 94         }
 95         try {
 96             file.createNewFile();
 97         } catch (IOException e) {
 98             e.printStackTrace();
 99         }
100         file=new File("e:/tradeno2.txt");
101         if (file.exists()){
102             file.delete();
103         }
104         try {
105             file.createNewFile();
106         } catch (IOException e) {
107             e.printStackTrace();
108         }
109 
110         new Thread(new Runnable() {
111             @Override
112             public void run() {
113 
114                 try {
115                     Writer w=new FileWriter("e:/tradeno1.txt");
116                     BufferedWriter buffWriter=new BufferedWriter(w);
117 
118                     TimeMark timeMark=new TimeMark();
119                     for (int i = 0; i < 12000; i++) {
120                         String x=getInstance().getNo();
121                         //System.out.println(x);
122                         buffWriter.write(x+"	
");
123                     }
124                     System.out.println("]]]");
125                     timeMark.simplePrint();
126 
127                     buffWriter.close();
128                     w.close();
129                     System.out.println("写入成功!");
130 
131                 } catch (FileNotFoundException e) {
132                     System.out.println("要读取的文件不存在:"+e.getMessage());
133                 } catch (IOException e) {
134                     System.out.println("文件读取错误:"+e.getMessage());
135                 }
136             }
137         }).start();
138 
139         new Thread(new Runnable() {
140             @Override
141             public void run() {
142                 try {
143                     Writer w=new FileWriter("e:/tradeno2.txt");
144                     BufferedWriter buffWriter=new BufferedWriter(w);
145 
146                     TimeMark timeMark=new TimeMark();
147                     for (int i = 0; i < 24000; i++) {
148                         String x=getInstance().getNo();
149                         //System.out.println(x);
150                         buffWriter.write(x+"	
");
151                     }
152                     System.out.println("]]]");
153                     timeMark.simplePrint();
154 
155                     buffWriter.close();
156                     w.close();
157                     System.out.println("写入成功!");
158 
159                 } catch (FileNotFoundException e) {
160                     System.out.println("要读取的文件不存在:"+e.getMessage());
161                 } catch (IOException e) {
162                     System.out.println("文件读取错误:"+e.getMessage());
163                 }
164             }
165         }).start();
166     }
167 }

接下来是C#

public class TradeNoGenerator
    {
        //基准时间截 (2017-01-01)
        private static DateTime initDateTime = new DateTime(2017, 1, 1);
        //每秒基准容量1000个
        private static int secondsSpace = 1000;
        //基准容量扩容倍数
        private static int secondsSpaceMulti = 10;
        //多少秒不用,重新设定原子初始值
        private static int secondsRefresh = 60;
        //原子初始值
        private static long initStamp = (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti;

        static TradeNoGenerator __TradeNoGenerator;
        private TradeNoGenerator()
        {
        }

        public static TradeNoGenerator GetInstance()
        {
            if (__TradeNoGenerator == null)
            {
                __TradeNoGenerator = new TradeNoGenerator();
            }
            return __TradeNoGenerator;
        }

        //线程安全
        /**
         * 用这个,用这个
         * @return
         * 生成结果例:11983017246298
         * 由三段组成,11983017,2462,98
         * 第一段11983017,位数会随着时间增长增加,计算方式为与initStamp相加,结果为到秒的时间戳
         * 第二段,2462,看容量决定位数,容量=secondsSpace*secondsSpaceMulti,原子增长字段
         * 第三段98,固定2位,随机数
         */
        public String GetNo()
        {
            long no = Interlocked.Increment(ref initStamp);
            //当前秒时间戳与基准时间戳秒级数量
            double currentSecond = DateTime.UtcNow.Subtract(initDateTime).TotalSeconds;
            //计数超过60秒未使用过,则重新给定初始值
            if (currentSecond - no / (secondsSpace * secondsSpaceMulti) >= secondsRefresh)
            {
                //重置原子计数原始值
                initStamp= (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti;
                no = Interlocked.Increment(ref initStamp);
            }
            //计算容量是否超出
            if (no - currentSecond * secondsSpace * secondsSpaceMulti >= secondsSpace * secondsSpaceMulti)
            {
                //超出则等待下一秒的到来,这里计算到下一整数秒的微秒差
                int millisWithinSecond = DateTime.UtcNow.Millisecond % 1000;
                try
                {
                    Thread.Sleep(1000 - millisWithinSecond);
                }
                catch (Exception ex)
                {
                    
                }

                //重置原子计数原始值
                initStamp = (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti;
                no = Interlocked.Increment(ref initStamp);
            }

            int hashCode = Guid.NewGuid().GetHashCode();
            hashCode = hashCode < 0 ? -hashCode : hashCode;
            String randomStr = hashCode.ToString().Substring(0, 2);
            return no + randomStr;
        }
    }

注释写得很清楚啦

1秒基准容量是1000个,可以以10的倍数来扩大(只能是10的倍数),一般扩大10倍,1秒容量到10000个,可以解决很多问题啦

速度你们可以自己测试,一秒内的速度是毫秒级别的

超出秒容量的话,那就得等待下一秒的到来

还有个问题就是当前不支持分布式

无论是JAVA还是C#基本是利用原子自增方式解决并发和效率问题

原文地址:https://www.cnblogs.com/bestfc/p/7447964.html