生成4位随机数

本来是用随机算法的,但是会有重复,花了点时间研究了下,发现雪花算法好似不会重复,

代码如下:

 

import java.util.concurrent.atomic.AtomicInteger;

public class MinuteCounter {
    private static final int MASK = 0x7FFFFFFF;
    private final AtomicInteger atom;
    
    public MinuteCounter() {
        atom = new AtomicInteger(0);
    }
    
    public final int incrementAndGet() {
        return atom.incrementAndGet() & MASK;
    }
    
    public int get() {
        return atom.get() & MASK;
    }
    
    public void set(int newValue) {
        atom.set(newValue & MASK);
    }
}

 

 

/**
 * 此方式只适用于一个单体应用,不适合分布式系统
 *
 *  将产生的Id类型更改为Integer 32bit <br>
 *  把时间戳的单位改为分钟,使用25个比特的时间戳(分钟) <br>
 *  去掉机器ID和数据中心ID <br>
 *  7个比特作为自增值,即2的7次方等于128。
 *
 */
public class SnowflakeIdWorker3rd {
    /** 开始时间戳 (2019-01-01) */
    private final int twepoch = 25771200;// 1546272000000L/1000/60;

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

    /** 时间截向左移7位 */
    private final long timestampLeftShift = sequenceBits;

    /** 生成序列的掩码,这里为127 */
    private final int sequenceMask = -1 ^ (-1 << sequenceBits);

    /** 分钟内序列(0~127) */
    private int sequence = 0;
    private int laterSequence = 0;

    /** 上次生成ID的时间戳 */
    private int lastTimestamp = -1;

    private final MinuteCounter counter = new MinuteCounter();
    
    /** 预支时间标志位 */
    boolean isAdvance = false;

    // ==============================Constructors=====================================
    public SnowflakeIdWorker3rd() {

    }

    // ==============================Methods==========================================
    /**
     * 获得下一个ID (该方法是线程安全的)
     * 
     * @return SnowflakeId
     */
    public synchronized int nextId() {
        
        
        int timestamp = timeGen();
        // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                    "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        
        if(timestamp > counter.get()) {
            counter.set(timestamp);
            isAdvance = false;
        }

        // 如果是同一时间生成的,则进行分钟内序列
        if (lastTimestamp == timestamp || isAdvance) {
            if(!isAdvance) {
                sequence = (sequence + 1) & sequenceMask;
            }

            // 分钟内自增列溢出
            if (sequence == 0) {
                // 预支下一个分钟,获得新的时间戳
                isAdvance = true;
                int laterTimestamp = counter.get();
                if (laterSequence == 0) {
                    laterTimestamp = counter.incrementAndGet();
                }

                int nextId = ((laterTimestamp - twepoch) << timestampLeftShift) //
                        | laterSequence;
                laterSequence = (laterSequence + 1) & sequenceMask;
                return nextId;
            }
        }
        // 时间戳改变,分钟内序列重置
        else {
            sequence = 0;
            laterSequence = 0;
        }

        // 上次生成ID的时间截
        lastTimestamp = timestamp;

        // 移位并通过或运算拼到一起组成32位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | sequence;
    }

    /**
     * 返回以分钟为单位的当前时间
     * 
     * @return 当前时间(分钟)
     */
    protected int timeGen() {
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000 / 60);
        return Integer.valueOf(timestamp);
    }

}

 

  

测试类

package com.excel.test;

import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        SnowflakeIdWorker3rd worker3rd = new SnowflakeIdWorker3rd();

        Set<Integer> set = new HashSet<>();

        for (int j = 0; j < 50; j++) {
            for (int i = 0; i < 100; i++) {
                int l = worker3rd.nextId();//% 10000;

                Thread.sleep(10);
                set.add(l);
            }
            System.out.println(set.size());
        }

    }
}

 

posted @ 2021-06-10 23:05  冬马党  阅读(804)  评论(0编辑  收藏  举报