五年沒寫過博客了,卻是每天在看java
轉來轉去,又轉回技術android
原來一直在使用微軟爸爸的東西,最近一兩年開始玩android,玩java,還有PostgreSQLc#
都有些應用了,卻是能夠整理些隨筆出來,這就是其中一篇吧安全
c#是java的優雅版本,java的linq就是一坨那啥,嗯!以爲不爽就別看了併發
訂單號這個玩意我想有幾點得保證dom
1,有順序但不連續分佈式
2,不能太長,最好15位之內ide
3,最好全數字測試
目前來講谷歌和百度出來的感受都達不到我要的這些要求,不是太長超過20位,就是會加上字母,再就是會連續,不然就沒順序,沒有辦法只有本身想ui
原本沒想本身乾的,這玩意絕對是個老問題,估計你們都有好辦法,藏着了吧
開幹,思路以下:
得有時間在裏面,這樣有順序
不連續好辦,加上隨機尾數
不能太長也好辦,取一部分時間戳,或者是當前時間減去過去一個固定時間的差值
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+"\t\r"); 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+"\t\r"); 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#基本是利用原子自增方式解決併發和效率問題