生成業(yè)務(wù)表ID

public class SnowflakeIdWorkerGenerator {

? ? private static final SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker();

? ? public static Long nextId() {

? ? ? ? return snowflakeIdWorker.nextId();

? ? }

? ? public static String nextStringId() {

? ? ? ? return String.valueOf(snowflakeIdWorker.nextId());

? ? }

? ? public static String createOrderNo() {

? ? ? ? return DateUtil.getDate("yyyyMMdd")+snowflakeIdWorker.nextId();

? ? }

//? ? public static void main(String[] args) {

//? ? ? ? System.out.println(DateUtil.getDate("yyyyMMdd")+snowflakeIdWorker.nextId());

//? ? }

}


import java.text.SimpleDateFormat;

import java.util.Date;

@SuppressWarnings("all")

public class SnowflakeIdWorker {

? ? // ============================== Constants===========================================

? ? private final static String ERROR_CLOCK_BACK = "時(shí)間回?fù)苁靥耄芙^為超出%d毫秒生成ID";

? ? private final static String ERROR_ATTR_LIMIT = "%s屬性的范圍為0-%d";

? ? private final static String MSG_UID_PARSE = "{\"UID\":\"%s\",\"timestamp\":\"%s\",\"workerId\":\"%d\",\"dataCenterId\":\"%d\",\"sequence\":\"%d\"}";

? ? private final static String DATE_PATTERN_DEFAULT = "yyyy-MM-dd HH:mm:ss";

? ? // ==============================Fields===========================================

? ? /** 開(kāi)始時(shí)間截 (2017-12-25)桐玻,用于用當(dāng)前時(shí)間戳減去這個(gè)時(shí)間戳,算出偏移量 */

? ? private final long twepoch = 1551582622686L;

? ? /** 機(jī)器id所占的位數(shù)(表示只允許workId的范圍為:0-1023) */

? ? private final long workerIdBits = 5L;

? ? /** 數(shù)據(jù)標(biāo)識(shí)id所占的位數(shù) */

? ? private final long datacenterIdBits = 5L;

? ? /** 支持的最大機(jī)器id,結(jié)果是31 (這個(gè)移位算法可以很快的計(jì)算出幾位二進(jìn)制數(shù)所能表示的最大十進(jìn)制數(shù)) */

? ? public final long maxWorkerId = -1L ^ (-1L << workerIdBits);

? ? /** 支持的最大數(shù)據(jù)標(biāo)識(shí)id巾表,結(jié)果是31 */

? ? private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

? ? /** 序列在id中占的位數(shù) (表示只允許sequenceId的范圍為:0-4095)*/

? ? private final long sequenceBits = 12L;

? ? /** 機(jī)器ID向左移12位 */

? ? private final long workerIdShift = sequenceBits;

? ? /** 數(shù)據(jù)標(biāo)識(shí)id向左移17位(12+5) */

? ? private final long datacenterIdShift = sequenceBits + workerIdBits;

? ? /** 時(shí)間截向左移22位(5+5+12) */

? ? private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

? ? /** 生成序列的掩碼,(防止溢出:位與運(yùn)算保證計(jì)算的結(jié)果范圍始終是 0-4095救崔,0b111111111111=0xfff=4095) */

? ? private final long sequenceMask = -1L ^ (-1L << sequenceBits);

? ? /** 工作機(jī)器ID(0~31) */

? ? private long workerId = 15;

? ? /** 數(shù)據(jù)中心ID(0~31) */

? ? private long datacenterId = 15;

? ? /** 毫秒內(nèi)序列(0~4095) */

? ? private long sequence = 0L;

? ? /** 上次生成ID的時(shí)間截 */

? ? private long lastTimestamp = -1L;

? ? private boolean isClock = false;

? ? public void setClock(boolean clock) {

? ? ? ? isClock = clock;

? ? }

? ? // ==============================Methods==========================================

/**

? ? * 獲得下一個(gè)ID (該方法是線程安全的)

*

? ? * @return SnowflakeId

*/

? ? public synchronized long nextId() {

? ? ? ? long timestamp = timeGen();

? ? ? ? // 閏秒:如果當(dāng)前時(shí)間小于上一次ID生成的時(shí)間戳锭部,說(shuō)明系統(tǒng)時(shí)鐘回退過(guò)這個(gè)時(shí)候應(yīng)當(dāng)拋出異常

? ? ? ? if (timestamp < lastTimestamp) {

? ? ? ? ? ? long offset = lastTimestamp - timestamp;

? ? ? ? ? ? if (offset <= 5) {

? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? // 時(shí)間偏差大小小于5ms,則等待兩倍時(shí)間

? ? ? ? ? ? ? ? ? ? wait(offset << 1);// wait

? ? ? ? ? ? ? ? ? ? timestamp = timeGen();

? ? ? ? ? ? ? ? ? ? if (timestamp < lastTimestamp) {

? ? ? ? ? ? ? ? ? ? ? ? // 還是小于篙顺,拋異常并上報(bào)

? ? ? ? ? ? ? ? ? ? ? ? throw new RuntimeException(String.format(ERROR_CLOCK_BACK, lastTimestamp - timestamp));

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? ? ? throw new RuntimeException(e);

? ? ? ? ? ? ? ? }

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? throw new RuntimeException(String.format(ERROR_CLOCK_BACK, lastTimestamp - timestamp));

? ? ? ? ? ? }

}

? ? ? ? // 解決跨毫秒生成ID序列號(hào)始終為偶數(shù)的缺陷:如果是同一時(shí)間生成的偶芍,則進(jìn)行毫秒內(nèi)序列

? ? ? ? if (lastTimestamp == timestamp) {

? ? ? ? ? ? // 通過(guò)位與運(yùn)算保證計(jì)算的結(jié)果范圍始終是0-4095

? ? ? ? ? ? sequence = (sequence + 1) & sequenceMask;

? ? ? ? ? ? // 毫秒內(nèi)序列溢出

? ? ? ? ? ? if (sequence == 0) {

? ? ? ? ? ? ? ? // 阻塞到下一個(gè)毫秒,獲得新的時(shí)間戳

? ? ? ? ? ? ? ? timestamp = tilNextMillis(lastTimestamp);

? ? ? ? ? ? }

? ? ? ? } else {

? ? ? ? ? ? // 時(shí)間戳改變,毫秒內(nèi)序列重置

? ? ? ? ? ? sequence = 0L;

? ? ? ? }

? ? ? ? // 上次生成ID的時(shí)間截

? ? ? ? lastTimestamp = timestamp;

? ? ? ? /*

? ? ? ? * 1.左移運(yùn)算是為了將數(shù)值移動(dòng)到對(duì)應(yīng)的段(41德玫、5匪蟀、5,12那段因?yàn)楸緛?lái)就在最右宰僧,因此不用左移)

? ? ? ? * 2.然后對(duì)每個(gè)左移后的值(la材彪、lb、lc琴儿、sequence)做位或運(yùn)算段化,是為了把各個(gè)短的數(shù)據(jù)合并起來(lái),合并成一個(gè)二進(jìn)制數(shù)? ? ? ? * 3.最后轉(zhuǎn)換成10進(jìn)制造成,就是最終生成的id(64位的ID)

*/

? ? ? ? return ((timestamp - twepoch) << timestampLeftShift) //

? ? ? ? ? ? ? ? | (datacenterId << datacenterIdShift) //

? ? ? ? ? ? ? ? | (workerId << workerIdShift) //

? ? ? ? ? ? ? ? | sequence;

? ? }

? ? /**

? ? * @方法名稱 parseUID

? ? * @功能描述 <pre>反解析UID

*/

? ? public String parseUID(Long uid) {

? ? ? ? long totalBits = 64L;// 總位數(shù)

? ? ? ? long signBits = 1L;// 標(biāo)識(shí)

? ? ? ? long timestampBits = 41L;// 時(shí)間戳

? ? ? ? // 解析Uid:標(biāo)識(shí) -- 時(shí)間戳 -- 數(shù)據(jù)中心 -- 機(jī)器碼 --序列

? ? ? ? long sequence = (uid << (totalBits - sequenceBits)) >>> (totalBits - sequenceBits);

? ? ? ? long dataCenterId = (uid << (timestampBits + signBits)) >>> (totalBits - datacenterIdBits);

? ? ? ? long workerId = (uid << (timestampBits + signBits + datacenterIdBits)) >>> (totalBits - workerIdBits);

? ? ? ? long deltaSeconds = uid >>> (datacenterIdBits + workerIdBits + sequenceBits);

? ? ? ? // 時(shí)間處理(補(bǔ)上開(kāi)始時(shí)間戳)

? ? ? ? Date thatTime = new Date(twepoch + deltaSeconds);

? ? ? ? String date = new SimpleDateFormat(DATE_PATTERN_DEFAULT).format(thatTime);

? ? ? ? // 格式化輸出

? ? ? ? return String.format(MSG_UID_PARSE, uid, date, workerId, dataCenterId, sequence);

? ? }

? ? /**

? ? * @方法名稱 parseUID

? ? * @功能描述 <pre>反解析UID(字符串截取显熏,性能相對(duì)差;不推薦使用)

*/

? ? public String parseUID(String uid) {

? ? ? ? uid = Long.toBinaryString(Long.parseLong(uid));

? ? ? ? int len = uid.length();

? ? ? ? // 解析Uid:標(biāo)識(shí) -- 時(shí)間戳 -- 數(shù)據(jù)中心 -- 機(jī)器碼 --序列

? ? ? ? int sequenceStart = len < workerIdShift ? 0 : (int)(len - workerIdShift);// sequence起始數(shù)

? ? ? ? int workerStart = len < datacenterIdShift ? 0 : (int)(len - datacenterIdShift);// worker起始數(shù)

? ? ? ? int timeStart = len < timestampLeftShift ? 0 : (int)(len - timestampLeftShift);// 時(shí)間起始數(shù)

? ? ? ? String sequence = uid.substring(sequenceStart, len);

? ? ? ? String workerId = sequenceStart == 0 ? "0" : uid.substring(workerStart, sequenceStart);

? ? ? ? String dataCenterId = workerStart == 0 ? "0" : uid.substring(timeStart, workerStart);

? ? ? ? // 時(shí)間處理(補(bǔ)上開(kāi)始時(shí)間戳)

? ? ? ? String time = timeStart == 0 ? "0" : uid.substring(0, timeStart);

? ? ? ? Date timeDate = new Date(Long.parseLong(time, 2) + twepoch);

? ? ? ? String date = new SimpleDateFormat(DATE_PATTERN_DEFAULT).format(timeDate);

? ? ? ? // 格式化輸出

? ? ? ? return String.format(MSG_UID_PARSE, uid, date, Integer.valueOf(workerId, 2), Integer.valueOf(dataCenterId, 2), Integer.valueOf(sequence, 2));

? ? }

? ? /**

? ? * 保證返回的毫秒數(shù)在參數(shù)之后(阻塞到下一個(gè)毫秒晒屎,直到獲得新的時(shí)間戳)

*

? ? * @param lastTimestamp 上次生成ID的時(shí)間截

? ? * @return 當(dāng)前時(shí)間戳

? ? */

? ? protected long tilNextMillis(long lastTimestamp) {

? ? ? ? long timestamp = timeGen();

? ? ? ? while (timestamp <= lastTimestamp) {

? ? ? ? ? ? timestamp = timeGen();

? ? ? ? }

? ? ? ? return timestamp;

? ? }

? ? /**

? ? * 獲得系統(tǒng)當(dāng)前毫秒數(shù)

? ? *

? ? * @return 當(dāng)前時(shí)間(毫秒)

*/

? ? protected long timeGen() {

? ? ? ? if (isClock) {

? ? ? ? ? ? // 解決高并發(fā)下獲取時(shí)間戳的性能問(wèn)題

? ? ? ? ? ? return new Date().getTime();

? ? ? ? } else {

? ? ? ? ? ? return System.currentTimeMillis();

? ? ? ? }

}

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末喘蟆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鼓鲁,更是在濱河造成了極大的恐慌蕴轨,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坐桩,死亡現(xiàn)場(chǎng)離奇詭異尺棋,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)膘螟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)成福,“玉大人,你說(shuō)我怎么就攤上這事荆残∨” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵内斯,是天一觀的道長(zhǎng)蕴潦。 經(jīng)常有香客問(wèn)我,道長(zhǎng)俘闯,這世上最難降的妖魔是什么潭苞? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮真朗,結(jié)果婚禮上此疹,老公的妹妹穿的比我還像新娘。我一直安慰自己遮婶,他們只是感情好蝗碎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著旗扑,像睡著了一般蹦骑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上臀防,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天眠菇,我揣著相機(jī)與錄音,去河邊找鬼清钥。 笑死琼锋,一個(gè)胖子當(dāng)著我的面吹牛放闺,可吹牛的內(nèi)容都是我干的祟昭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼怖侦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼篡悟!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起匾寝,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤搬葬,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后艳悔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體急凰,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年猜年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抡锈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疾忍。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖床三,靈堂內(nèi)的尸體忽然破棺而出一罩,到底是詐尸還是另有隱情,我是刑警寧澤撇簿,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布聂渊,位于F島的核電站,受9級(jí)特大地震影響四瘫,放射性物質(zhì)發(fā)生泄漏汉嗽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一找蜜、第九天 我趴在偏房一處隱蔽的房頂上張望诊胞。 院中可真熱鬧,春花似錦锹杈、人聲如沸撵孤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)邪码。三九已至,卻和暖如春咬清,著一層夾襖步出監(jiān)牢的瞬間闭专,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工旧烧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留影钉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓掘剪,卻偏偏與公主長(zhǎng)得像平委,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夺谁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355