All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.fantasticmao.mundo.data.support.Snowflake Maven / Gradle / Ivy

There is a newer version: 1.0.10
Show newest version
package cn.fantasticmao.mundo.data.support;

import javax.annotation.concurrent.ThreadSafe;
import java.util.Arrays;

/**
 * Snowflake
 *
 * @author fantasticmao
 * @version 1.0
 * @since 2018/7/22
 */
public interface Snowflake {

    long nextId();

    /**
     * TwitterSnowflake
     *
     * @see Announcing Snowflake
     * @see twitter-archive snowflake
     */
    @ThreadSafe
    class TwitterSnowflake implements Snowflake {
        private static final int BIT_NOT_USED = 1; // 尚未使用
        private static final int BIT_TIMESTAMP = 41; // 时间戳占用位数
        private static final int BIT_WORKER_NUMBER = 10; // 机器号占用位数
        private static final int BIT_SEQUENCE_NUMBER = 12; // 序列号占用位数

        private static final int LEFT_SEQUENCE_NUMBER = 0; // 左偏移量:序列号
        private static final int LEFT_WORKER_NUMBER = LEFT_SEQUENCE_NUMBER + BIT_SEQUENCE_NUMBER; // 左偏移量:机器号
        private static final int LEFT_TIMESTAMP = LEFT_WORKER_NUMBER + BIT_WORKER_NUMBER; // 左偏移量:时间戳

        private static final int RIGHT_TIMESTAMP = BIT_NOT_USED; // 右偏移量:时间戳
        private static final int RIGHT_WORKER_NUMBER = RIGHT_TIMESTAMP + BIT_TIMESTAMP; // 右偏移量:机器号
        private static final int RIGHT_SEQUENCE_NUMBER = RIGHT_WORKER_NUMBER + BIT_WORKER_NUMBER; // 右偏移量:序列号

        private static final long MAX_TIMESTAMP = -1L ^ (-1L << BIT_TIMESTAMP); // 最大值:时间戳
        private static final long MAX_WORKER_NUMBER = -1L ^ (-1L << BIT_WORKER_NUMBER); // 最大值:机器号
        private static final long MAX_SEQUENCE_NUMBER = -1L ^ (1L << BIT_SEQUENCE_NUMBER); // 最大值:序列号

        private static final long START_TIMESTAMP = 1546272000000L; // Tue Jan 01 2019 00:00:00 GMT+0800 (China Standard Time)

        private final long workerNumber;
        private long lastTimestamp;
        private long sequence;

        private TwitterSnowflake(long workerNumber) {
            this.workerNumber = workerNumber;
            this.lastTimestamp = 0L;
            this.sequence = 0L;
        }

        public static Snowflake getInstance(long workerNumber) {
            if (workerNumber < 0 || workerNumber > MAX_WORKER_NUMBER) {
                throw new IllegalArgumentException();
            }
            return new TwitterSnowflake(workerNumber);
        }

        public static String toBinaryString(long id) {
            final char[] chars = new char[64];
            Arrays.fill(chars, '0');
            String idBinaryString = Long.toBinaryString(id);
            idBinaryString.getChars(0, idBinaryString.length(), chars, 64 - idBinaryString.length());

            final String binaryString = new String(chars);
            return String.format("raw id: %s", id) + System.lineSeparator() +
                String.format("binary string: %s", binaryString) + System.lineSeparator() +
                String.format("segment string: %s, %s, %s, %s",
                    binaryString.substring(0, TwitterSnowflake.RIGHT_TIMESTAMP),
                    binaryString.substring(TwitterSnowflake.RIGHT_TIMESTAMP, TwitterSnowflake.RIGHT_WORKER_NUMBER),
                    binaryString.substring(TwitterSnowflake.RIGHT_WORKER_NUMBER, TwitterSnowflake.RIGHT_SEQUENCE_NUMBER),
                    binaryString.substring(TwitterSnowflake.RIGHT_SEQUENCE_NUMBER, 64));
        }

        @Override
        public synchronized long nextId() {
            long timestamp = this.timeGen();
            if (timestamp < lastTimestamp) {
                throw new IllegalArgumentException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                    lastTimestamp - timestamp));
            }

            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE_NUMBER;
                if (sequence == 0) {
                    timestamp = this.tilNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0;
            }

            lastTimestamp = timestamp;

            return (timestamp - START_TIMESTAMP) << LEFT_TIMESTAMP | workerNumber << LEFT_WORKER_NUMBER | sequence;
        }

        private long timeGen() {
            return System.currentTimeMillis();
        }

        private long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy