} catch (Exception e) { // 若是redis宕机就采用uuid的方式
int first = new Random(10).nextInt(8) + 1;
int randNo=UUID.randomUUID().toString().hashCode();
if (randNo < 0) {
randNo=-randNo;
}
return Long.va 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》开源 lueOf(first + String.format(“d”, randNo));
}
}
我把电脑移回给面试官,他很快的扫了一下我的代码,说了一句。
面试官:小伙子,不写注释哦,这个习惯不好哦。
我:哦哦,谢谢提醒,不好意思,下次我会注意的。
雪花算法
我:第六种方式是「雪花算法」,也是现在市面上比较流行的生成分布式ID的方法。
说着说着,我知道画图又是必不可少的了,于是在桌子上有画了起来,面试官好奇的看看我,知道了我在干啥,又耐心的等了等。
我:他是采用64bit作为ID生成类型,并且将64bit划分为,如下图的几段。
我顺手把我画的图递给他看了看,接着对着这个图进行解释。
我:第一位作为标识位,因为Java中long类型的时代符号的,因为ID位正数,所以第一位位0。
我:接着的41bit是时间戳,毫秒级位单位,注意这里的时间戳并不是指当前时间的时间戳,而是值之间差(「当前时间-开始时间」)。
我:这里的开始时间一般是指ID生成器的开始时间,是由我们程序自己指定的。
我:接着后面的10bit:包括5位的「数据中心标识ID(datacenterId)和5位的机器标识ID(workerId)」,可以最多标识1024个节点(1<<10=1024)。
我:最的12位是序列号,12位的计数顺序支持每个节点每毫秒差生4096序列号(1<<12=4096)。
我:雪花算法使用数据中心ID和机器ID作为标识,不会产生ID的重复,并且是在本地生成,不会消耗网络,效率高,有数据显示,每秒能生成26万个ID。
我:但是雪花算法也是又自己的缺点,因为雪花算法的计算依赖于时间,若是系统时间回拨,就会产生重复ID的情况。
面试官:那对于时间回拨产生重复ID的情况,你有什么比较好的解决方案吗?
我:在雪花算法的实现中,若是其前置的时间等于当前的时间,就抛出异常,也可以关闭掉时间回拨。
我:对于回拨时间比较短的,可以等待回拨时间过后再生成ID。
面试官:你可以帮我敲一个雪花算法吗?我这键盘给你。
我:……
我:好的。
时间流逝中…
过了几分钟时间,也总算是把雪花算法给敲出来了,真正要老命,面个试怎么就那么难呢?
/**
* 雪花算法
* @author:黎杜
*/
public class SnowflakeIdWorker {
/** 开始时间截 */
private final long twepoch = 1530051700000L;
/** 机器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);
/** 序列的位数 */
private final long sequenceBits = 12L;
/** 机器ID向左移12位 */
private final long workerIdShift = sequenceBits;
/** 数据标识id向左移17位 */
private final long datacenterIdShift = sequenceBits + workerIdBits;
/** 时间截向左移22位*/
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
/** 生成序列的掩码 */
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/** 工作机器ID(0~31) */
private long workerId;
/** 数据中心ID(0~31) */
private long datacenterId;
/** 毫秒内序列(0~4095) */
private long sequence = 0L;
/** 上次生成ID的时间截 */
private long lastTimestamp = -1L;
/**
* 构造函数
* @param workerId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long workerId, long datacenterId) {
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));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* 获得下一个ID (该方法是线程安全的)
* @return SnowflakeId
*/
public synchronized long nextId() {
long timestamp = getCurrentTime();
//如果当前时间小于上一次生成的时间戳,说明系统时钟回退过就抛出异常
if (timestamp < lastTimestamp) {
throw new BusinessionException(“回拨的时间为:”+lastTimestamp - timestamp);
}
//如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出
if (sequence == 0) {
//获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
} else { //时间戳改变,毫秒内序列重置
sequence = 0L;
}
//上次生成ID的时间截
lastTimestamp = timestamp;
//移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift) // 计算时间戳
| (datacenterId << datacenterIdShift) // 计算数据中心
| (workerId << workerIdShift) // 计算机器ID
| sequence; // 序列号
}
/**
*获得新的时间戳
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = getCurrentTime();
// 若是当前时间等于上一次的1时间就一直阻塞,知道获取到最新的时间(回拨后的时间)
while (timestamp <= lastTimestamp) {
timestamp = getCurrentTime();
}
return timestamp;
}
/**
* 获取当前时间
* @return 当前时间(毫秒)
*/
prote Java开源项目【ali1024.coding.net/public/P7/Java/git】 cted long getCurrentTime() {
return System.currentTimeMillis();
}
为了给面试官留下个好印象,这下也写上了注解,免得他又说我,敲完我又把电脑移回给他,他快速的看了看,点了点头,嘴角露出思思的笑意。
面试官:嗯,你的底子还算比价扎实,面试之前早有准备吧,看了很多的面试资料。
我心想怎么是面试之前准备呢?我是一直再准备,从工作到现在都在总结自己的知识点,形成自己的知识体系,为了迎合他,也只能说是。
我:嗯嗯,是的,准备了很久,算是比较充分。
面试官:嗯,最后的两种算法,你还深入了解吗?
Leaf和UidGenerator
分享
这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!
Spring Cloud实战
Spring Boot实战
面试题整理(性能优化+微服务+并发编程+开源框架+分布式)
合他,也只能说是。
我:嗯嗯,是的,准备了很久,算是比较充分。
面试官:嗯,最后的两种算法,你还深入了解吗?
Leaf和UidGenerator
分享
这次面试我也做了一些总结,确实还有很多要学的东西。相关面试题也做了整理,可以分享给大家,了解一下面试真题,想进大厂的或者想跳槽的小伙伴不妨好好利用时间来学习。学习的脚步一定不能停止!
[外链图片转存中…(img-dNjnfAP3-1650622270135)]
Spring Cloud实战
[外链图片转存中…(img-jpD6uM3D-1650622270136)]
Spring Boot实战
[外链图片转存中…(img-wiX0Yb35-1650622270136)]
面试题整理(性能优化+微服务+并发编程+开源框架+分布式)