OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5013|回复: 0

Java中生成唯一ID的方法{记得关注哦}

[复制链接]

143

主题

145

帖子

0

精华

高级会员

Rank: 4

积分
585
金钱
585
注册时间
2020-5-25
在线时间
42 小时
发表于 2021-1-19 16:50:02 | 显示全部楼层 |阅读模式
有时我们不依赖于数据库中自动递增的字段产生唯一ID,比如多表同一字段需要统一一个唯一ID,这时就需要用程序来生成一个唯一的全局ID。UUID从Java 5开始, UUID 类提供了一种生成唯一ID的简单方法。UUID是通用唯一识别码 (Universally Unique Identifier)的缩写,UUID来源于OSF(Open Software Foundation,开源软件基金会)的DCE(Distributed Computing Environment,分布式计算环境)规范。UUID 的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的 UUID。UUID是一个128bit的数字,也可以表现为32个16进制的字符(每个字符0-F的字符代表4bit),中间用"-"分割。时间戳+UUID版本号: 分三段占16个字符(60bit+4bit),Clock Sequence号与保留字段:占4个字符(13bit+3bit),节点标识:占12个字符(48bit),UUID的唯一缺陷在于生成的结果串会比较长。public class GenerateUUID {    public static final void main(String... args) {        // generate random UUIDs        UUID idOne = UUID.randomUUID();        UUID idTwo = UUID.randomUUID();        log("UUID One: " + idOne);        log("UUID Two: " + idTwo);    }    private static void log(Object object) {        System.out.println(String.valueOf(object));    }}结果为UUID One: 6b193443-b95d-4462-9902-a6455ebc56d6UUID Two: 4ef9b375-839b-4150-8f31-1ed85fab63fd随机数的哈希值此方法使用SecureRandom和MessageDigest:启动时,初始化SecureRandom (这可能是一个冗长的操作)使用 SecureRandom生成一个随机数创建一个MessageDigest,使用某种摘要算法将MessageDigest返回的byte[]编码为某种可接受的文本形式检查结果是否已经被使用;如果尚未使用,则适合作为唯一标识符MessageDigest类是适合于产生任意数据的“单向散列”。public class GenerateId {    public static void main(String... arguments) {        try {            SecureRandom prng = SecureRandom.getInstance("SHA1PRNG");            String randomNum = Integer.valueOf(prng.nextInt()).toString();            MessageDigest sha = MessageDigest.getInstance("SHA-1");            byte[] result = sha.digest(randomNum.getBytes());            System.out.println("Random number: " + randomNum);            System.out.println("Message digest: " + hexEncode(result));        } catch (NoSuchAlgorithmException ex) {            System.err.println(ex);        }    }    static private String hexEncode(byte[] input) {        StringBuilder result = new StringBuilder();        char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',                'b', 'c', 'd', 'e', 'f'};        for (int idx = 0; idx < input.length; ++idx) {            byte b = input[idx];            result.append(digits[(b & 0xf0) >> 4]);            result.append(digits[b & 0x0f]);        }        return result.toString();    }}结果为Random number: -2017013782Message digest: 2c3bba8d4dbd3699648c5909685d21f9c64b6a8aTwitter的snowflaketwitter的一个全局唯一id生成器,结果是一个long型的ID。正数位(1bit):一个符号位,永远是0。时间戳(41bit) :自从2012年以来的毫秒数,能撑139年。自增序列(12bit,最大值4096):毫秒之内的自增,过了一毫秒会重新置0。DataCenter ID (5 bit, 最大值32):配置值,支持多机房。Worker ID ( 5 bit, 最大值32),配置值,一个机房里最多32个机器。Snowflake算法的变化Snowflake算法生成的唯一ID为long型数值,但如果想在应用中使用int类型的自增ID的话可以做些调整。时间戳改为分钟(25bit),自增序列(7bit)。自增序列最大值128,在一分钟内会不够使用。可以采用预支方式取下一分钟。此方式只适用于一个单体应用,不适合分布式系统。/** * @ClassName: SnowflakeIdWorker3rd * @Description:snowflake算法改进 * @author: wanghao * @date: 2019年12月13日 下午12:50:47 * @version V1.0 *  *          将产生的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);    }    // ==============================Test=============================================    /** 测试 */    public static void main(String[] args) {        SnowflakeIdWorker3rd idWorker = new SnowflakeIdWorker3rd();        for (int i = 0; i < 1000; i++) {            long id = idWorker.nextId();            System.out.println(i + ": " + id);        }    }}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);    }}java-环境搭建及基础语法http://www.makeru.com.cn/live/1394_202.html?s=143793java-数据类型http://www.makeru.com.cn/live/1394_203.html?s=143793java中读取配置文件的几种方式http://www.makeru.com.cn/live/1394_481.html?s=143793
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 12:35

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表