最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java算法之靜態(tài)內(nèi)部類實現(xiàn)雪花算法

 更新時間:2021年05月11日 11:30:24   作者:雨點的名字  
這篇文章主要介紹了java算法之靜態(tài)內(nèi)部類實現(xiàn)雪花算法,對算法感興趣的同學(xué),一定要看一下

概述

在生成表主鍵ID時,我們可以考慮主鍵自增 或者 UUID,但它們都有很明顯的缺點

主鍵自增:1、自增ID容易被爬蟲遍歷數(shù)據(jù)。2、分表分庫會有ID沖突。

UUID: 1、太長,并且有索引碎片,索引多占用空間的問題 2、無序。

雪花算法就很適合在分布式場景下生成唯一ID,它既可以保證唯一又可以排序。為了提高生產(chǎn)雪花ID的效率,

在這里面數(shù)據(jù)的運算都采用的是位運算

一、概念

1、原理

SnowFlake算法生成ID的結(jié)果是一個64bit大小的整數(shù),它的結(jié)構(gòu)如下圖:

算法描述:

1bit 因為二進(jìn)制中最高位是符號位,1表示負(fù)數(shù),0表示正數(shù)。生成的ID都是正整數(shù),所以最高位固定為0。

41bit-時間戳 精確到毫秒級,41位的長度可以使用69年。時間位還有一個很重要的作用是可以根據(jù)時間進(jìn)行排序。

10bit-工作機器id 10位的機器標(biāo)識,10位的長度最多支持部署1024個節(jié)點。

12bit-序列號 序列號即一系列的自增id,可以支持同一節(jié)點同一毫秒生成多個ID序號。
12位(bit)可以表示的最大正整數(shù)是,即可以用0、1、2、3、....4094這4095個數(shù)字,來表示同一機器同一時間截(毫秒)內(nèi)產(chǎn)生的4095個ID序號。

說明 由于在Java中64bit的整數(shù)是long類型,所以在Java中SnowFlake算法生成的id就是long來存儲的。

二、靜態(tài)類部類單例模式生產(chǎn)雪花ID代碼

下面生成雪花ID的代碼可以用于線上分布式項目中來生成分布式主鍵ID,因為設(shè)計采用的靜態(tài)內(nèi)部類的單例模式,通過加synchronized鎖來保證在

同一個服務(wù)器線程安全。至于不同服務(wù)器其實是不相關(guān)的,因為它們的機器碼是不一致的,所以就算同一時刻兩臺服務(wù)器都產(chǎn)生了雪花ID,那也不會一樣的。

1、代碼

public class SnowIdUtils {
    /**
     * 私有的 靜態(tài)內(nèi)部類
     */
    private static class SnowFlake {

        /**
         * 內(nèi)部類對象(單例模式)
         */
        private static final SnowIdUtils.SnowFlake SNOW_FLAKE = new SnowIdUtils.SnowFlake();
        /**
         * 起始的時間戳
         */
        private final long START_TIMESTAMP = 1557489395327L;
        /**
         * 序列號占用位數(shù)
         */
        private final long SEQUENCE_BIT = 12;
        /**
         * 機器標(biāo)識占用位數(shù)
         */
        private final long MACHINE_BIT = 10;
        /**
         * 時間戳位移位數(shù)
         */
        private final long TIMESTAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT;
        /**
         * 最大序列號  (4095)
         */
        private final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
        /**
         * 最大機器編號 (1023)
         */
        private final long MAX_MACHINE_ID = ~(-1L << MACHINE_BIT);
        /**
         * 生成id機器標(biāo)識部分
         */
        private long machineIdPart;
        /**
         * 序列號
         */
        private long sequence = 0L;
        /**
         * 上一次時間戳
         */
        private long lastStamp = -1L;

        /**
         * 構(gòu)造函數(shù)初始化機器編碼
         */
        private SnowFlake() {
            //模擬這里獲得本機機器編碼
            long localIp = 4321;
            //localIp & MAX_MACHINE_ID最大不會超過1023,在左位移12位
            machineIdPart = (localIp & MAX_MACHINE_ID) << SEQUENCE_BIT;
        }
        /**
         * 獲取雪花ID
         */
        public synchronized long nextId() {
            long currentStamp = timeGen();
            //避免機器時鐘回?fù)?
            while (currentStamp < lastStamp) {
                // //服務(wù)器時鐘被調(diào)整了,ID生成器停止服務(wù).
                throw new RuntimeException(String.format("時鐘已經(jīng)回?fù)?  Refusing to generate id for %d milliseconds", lastStamp - currentStamp));
            }
            if (currentStamp == lastStamp) {
                // 每次+1
                sequence = (sequence + 1) & MAX_SEQUENCE;
                // 毫秒內(nèi)序列溢出
                if (sequence == 0) {
                    // 阻塞到下一個毫秒,獲得新的時間戳
                    currentStamp = getNextMill();
                }
            } else {
                //不同毫秒內(nèi),序列號置0
                sequence = 0L;
            }
            lastStamp = currentStamp;
            //時間戳部分+機器標(biāo)識部分+序列號部分
            return (currentStamp - START_TIMESTAMP) << TIMESTAMP_LEFT | machineIdPart | sequence;
        }
        /**
         * 阻塞到下一個毫秒,直到獲得新的時間戳
         */
        private long getNextMill() {
            long mill = timeGen();
            //
            while (mill <= lastStamp) {
                mill = timeGen();
            }
            return mill;
        }
        /**
         * 返回以毫秒為單位的當(dāng)前時間
         */
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    }

    /**
     * 獲取long類型雪花ID
     */
    public static long uniqueLong() {
        return SnowIdUtils.SnowFlake.SNOW_FLAKE.nextId();
    }
    /**
     * 獲取String類型雪花ID
     */
    public static String uniqueLongHex() {
        return String.format("%016x", uniqueLong());
    }

    /**
     * 測試
     */
    public static void main(String[] args) throws InterruptedException {
        //計時開始時間
        long start = System.currentTimeMillis();
        //讓100個線程同時進(jìn)行
        final CountDownLatch latch = new CountDownLatch(100);
        //判斷生成的20萬條記錄是否有重復(fù)記錄
        final Map<Long, Integer> map = new ConcurrentHashMap();
        for (int i = 0; i < 100; i++) {
            //創(chuàng)建100個線程
            new Thread(() -> {
                for (int s = 0; s < 2000; s++) {
                    long snowID = SnowIdUtils.uniqueLong();
                    log.info("生成雪花ID={}",snowID);
                    Integer put = map.put(snowID, 1);
                    if (put != null) {
                        throw new RuntimeException("主鍵重復(fù)");
                    }
                }
                latch.countDown();
            }).start();
        }
        //讓上面100個線程執(zhí)行結(jié)束后,在走下面輸出信息
        latch.await();
        log.info("生成20萬條雪花ID總用時={}", System.currentTimeMillis() - start);
    }
}

2、測試結(jié)果

從圖中我們可以得出

1、在100個線程并發(fā)下,生成20萬條雪花ID的時間大概在1.6秒左右,所有所性能還是蠻ok的。

2、生成20萬條雪花ID并沒有一條相同的ID,因為有一條就會拋出異常了。

3、為什么說41位時間戳最長只能有69年

我們思考41的二進(jìn)制,最大值也就41位都是1,也就是也就是說41位可以表示個毫秒的值,轉(zhuǎn)化成單位年則是

我們可以通過代碼泡一下就知道了。

public static void main(String[] args) {
    //41位二進(jìn)制最小值
    String minTimeStampStr = "00000000000000000000000000000000000000000";
    //41位二進(jìn)制最大值
    String maxTimeStampStr = "11111111111111111111111111111111111111111";
    //轉(zhuǎn)10進(jìn)制
    long minTimeStamp = new BigInteger(minTimeStampStr, 2).longValue();
    long maxTimeStamp = new BigInteger(maxTimeStampStr, 2).longValue();
    //一年總共多少毫秒
    long oneYearMills = 1L * 1000 * 60 * 60 * 24 * 365;
    //算出最大可以多少年
    System.out.println((maxTimeStamp - minTimeStamp) / oneYearMills);
}

運行結(jié)果

所以說雪花算法生成的ID,只能保證69年內(nèi)不會重復(fù),如果超過69年的話,那就考慮換個服務(wù)器部署吧,并且要保證該服務(wù)器的ID和之前都沒有重復(fù)過。

以上就是java算法之靜態(tài)內(nèi)部類實現(xiàn)雪花算法的詳細(xì)內(nèi)容,更多關(guān)于java算法的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java并發(fā)編程之Executor接口的使用

    Java并發(fā)編程之Executor接口的使用

    今天給大家?guī)淼氖顷P(guān)于Java并發(fā)編程的相關(guān)知識,文章圍繞著Executor接口的使用展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • JWT Token實現(xiàn)方法及步驟詳解

    JWT Token實現(xiàn)方法及步驟詳解

    這篇文章主要介紹了JWT Token實現(xiàn)方法及步驟詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • Java 中引入內(nèi)部類的意義?

    Java 中引入內(nèi)部類的意義?

    這篇文章主要介紹了Java 中引入內(nèi)部類的意義?文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,,需要的朋友可以參考下
    2019-06-06
  • Java8語法糖之Lambda表達(dá)式的深入講解

    Java8語法糖之Lambda表達(dá)式的深入講解

    這篇文章主要給大家介紹了關(guān)于Java8語法糖之Lambda表達(dá)式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • Java之Set?交集,差集,并集的用法

    Java之Set?交集,差集,并集的用法

    這篇文章主要介紹了Java之Set?交集,差集,并集的用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java實戰(zhàn)寵物醫(yī)院預(yù)約掛號系統(tǒng)的實現(xiàn)流程

    Java實戰(zhàn)寵物醫(yī)院預(yù)約掛號系統(tǒng)的實現(xiàn)流程

    只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+JSP+Spring+SpringBoot+MyBatis+html+layui+maven+Mysql實現(xiàn)一個寵物醫(yī)院預(yù)約掛號系統(tǒng),大家可以在過程中查缺補漏,提升水平
    2022-01-01
  • Spring Boot快速入門教程

    Spring Boot快速入門教程

    本篇文章主要介紹了Spring Boot快速入門教程,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • SpringBoot指定激活配置文件的方法

    SpringBoot指定激活配置文件的方法

    Spring Boot 對多環(huán)境整合已經(jīng)有了很好的支持,能夠在運行間、打包時自由切換環(huán)境,這篇文章主要介紹了SpringBoot指定激活配置文件,需要的朋友可以參考下
    2023-11-11
  • springboot對象為null的屬性在json中不顯示的解決

    springboot對象為null的屬性在json中不顯示的解決

    這篇文章主要介紹了springboot對象為null的屬性在json中不顯示的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • java詞法分析器DDL遞歸應(yīng)用詳解

    java詞法分析器DDL遞歸應(yīng)用詳解

    這篇文章主要介紹了java詞法分析器DDL遞歸應(yīng)用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07

最新評論

嘉义市| 合阳县| 新绛县| 望奎县| 广饶县| 布拖县| 武山县| 中卫市| 中阳县| 顺昌县| 嘉兴市| 张掖市| 南城县| 手机| 会理县| 涟源市| 新昌县| 漯河市| 中卫市| 徐水县| 敖汉旗| 泰和县| 南部县| 三河市| 泾源县| 黄石市| 莱州市| 乐都县| 济宁市| 社会| 西乌| 类乌齐县| 台北市| 突泉县| 织金县| 湘阴县| 秭归县| 萝北县| 和田县| 行唐县| 万载县|