日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

雪花算法工具類

作者:枯楓葉 更新時間: 2022-08-05 編程語言

1,導入工具類

package com.sport.finetrackserver.common.utils;

import cn.hutool.core.convert.Convert;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @author zdj
 * @version 1.0
 * @date 2022/05/30 15:35
 */
public class SnowFlakeUtil {

    // ==============================Fields===========================================
    /**
     * 開始時間截 (2021-01-19)
     */
    private final long twepoch = 1611043703161L;

    /**
     * 機器id所占的位數
     */
    private final long workerIdBits = 5L;

    /**
     * 數據標識id所占的位數
     */
    private final long dataCenterIdBits = 5L;

    /**
     * 支持的最大機器id,結果是31 (這個移位算法可以很快的計算出幾位二進制數所能表示的最大十進制數)
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 支持的最大數據標識id,結果是31
     */
    private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);

    /**
     * 序列在id中占的位數
     */
    private final long sequenceBits = 12L;

    /**
     * 機器ID向左移12位
     */
    private final long workerIdShift = sequenceBits;

    /**
     * 數據標識id向左移17位(12+5)
     */
    private final long dataCenterIdShift = sequenceBits + workerIdBits;

    /**
     * 時間截向左移22位(5+5+12)
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    /**
     * 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095)
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 工作機器ID(0~31)
     */
    private long workerId;

    /**
     * 數據中心ID(0~31)
     */
    private long dataCenterId;

    /**
     * 備用的數據中心ID(0~31),當時鐘回撥時,為了不拋異常,啟用備用ID
     */
    private long standbyDatacenterId;

    /**
     * 毫秒內序列(0~4095)
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的時間截
     */
    private long lastTimestamp = -1L;

    /**
     * 是否時鐘回撥
     */
    private boolean isTimestampBack = false;


    // ==============================Constructors=====================================
    /**
     * 最大容忍時間, 單位毫秒, 即如果時鐘只是回撥了該變量指定的時間, 那么等待相應的時間即可;
     * 考慮到sequence服務的高性能, 這個值不易過大
     */
    private static final long MAX_BACKWARD_MS = 3;

    /**
     * 構造函數
     * @param workerId     工作ID (0~31)
     * @param datacenterId 數據中心ID (0~31)
     */
    SnowFlakeUtil(long workerId, long datacenterId, long standbyDatacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDataCenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDataCenterId));
        }
        if (standbyDatacenterId > maxDataCenterId || standbyDatacenterId < 0) {
            throw new IllegalArgumentException(String.format("standby datacenter Id can't be greater than %d or less than 0", maxDataCenterId));
        }
        if (datacenterId == standbyDatacenterId) {
            throw new IllegalArgumentException("datacenter Id can't equal to standby datacenter Id.");
        }
        this.workerId = workerId;
        this.dataCenterId = datacenterId;
        this.standbyDatacenterId = standbyDatacenterId;
    }


    // ==============================Methods==========================================
    /**
     * 獲得下一個ID (該方法是線程安全的)
     * @return SnowflakeId
     */
    synchronized long nextId() {
        long timestamp = timeGen();
        // 如果當前時間小于上一次ID生成的時間戳,說明系統時鐘回退過
        if (timestamp < lastTimestamp) {
            // 如果時鐘回撥在可接受范圍內, 等待即可
            long offset = lastTimestamp - timestamp;
            if (offset <= MAX_BACKWARD_MS) {
                try {
                    //睡(lastTimestamp - currentTimestamp)ms讓其追上
                    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(offset));

                    timestamp = timeGen();
                    //如果當前時間還是小于上一次ID生成的時間戳,這時啟用備用的datacenterId
                    if (timestamp < lastTimestamp) {
                        isTimestampBack = true;
                        //服務器時鐘被調整了
                        //log.error(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
                        //throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
                    } else {
                        isTimestampBack = false;
                    }
                } catch (Exception e) {
                    //log.error(e);
                }
            }
        }

        // 如果是同一時間生成的,則進行毫秒內序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            // 毫秒內序列溢出
            if (sequence == 0) {
                // 阻塞到下一個毫秒,獲得新的時間戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        // 時間戳改變,毫秒內序列重置
        else {
            sequence = 0L;
        }

        // 上次生成ID的時間截
        lastTimestamp = timestamp;

        //要使用的datacenterId
        long datacenterIdToUse = isTimestampBack ? standbyDatacenterId : dataCenterId;

        // 移位并通過或運算拼到一起組成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterIdToUse << dataCenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }

    /**
     * 阻塞到下一個毫秒,直到獲得新的時間戳
     * @param lastTimestamp 上次生成ID的時間截
     * @return 當前時間戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒為單位的當前時間
     * @return 當前時間(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 使用雙重校驗獲取實例對象
     */
    private volatile static SnowFlakeUtil snowflakeIdWorker;
    public static SnowFlakeUtil getInstance() {
        if (snowflakeIdWorker == null) {
            synchronized (SnowFlakeUtil.class) {
                if (snowflakeIdWorker == null) {
                    snowflakeIdWorker = new SnowFlakeUtil(1, 0,11);
                }
            }
        }
        return snowflakeIdWorker;
    }

    /**
     * 獲得下一個ID (該方法是線程安全的)
     * @return SnowflakeId
     */
    public static String getNextId() {
        return Convert.toStr(getInstance().nextId());
    }
}

2,使用:
在需要的地方直接使用即可:
在這里插入圖片描述

原文鏈接:https://blog.csdn.net/gelinwangzi_juge/article/details/125045353

欄目分類
最近更新