java 有穷自动机_Java实现雪花算法(snowflake)
本文主要介紹了Java實現雪花算法(snowflake),分享給大家,具體如下:
簡單描述
最高位是符號位,始終為0,不可用。
41位的時間序列,精確到毫秒級,41位的長度可以使用69年。時間位還有一個很重要的作用是可以根據時間進行排序。注意,41位時間截不是存儲當前時間的時間截,而是存儲時間截的差值(當前時間截 - 開始時間截) 后得到的值,這里的的開始時間截,一般是我們的id生成器開始使用的時間,由我們程序來指定的(如下下面程序SnowFlake類的START_STMP屬性)。41位的時間截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
10位的機器標識,10位的長度最多支持部署1024個節點。
12位的計數序列號,序列號即一系列的自增id,可以支持同一節點同一毫秒生成多個ID序號,12位的計數序列號支持每個節點每毫秒產生4096個ID序號。
加起來剛好64位,為一個Long型。這個算法很簡潔,但依舊是一個很好的ID生成策略。其中,10位器標識符一般是5位IDC+5位machine編號,唯一確定一臺機器。
算法實現
public class SnowFlake {
// 起始的時間戳
private final static long START_STMP = 1577808000000L; //2020-01-01
// 每一部分占用的位數,就三個
private final static long SEQUENCE_BIT = 12; //序列號占用的位數
private final static long MACHINE_BIT = 5; //機器標識占用的位數
private final static long DATACENTER_BIT = 5; //數據中心占用的位數
// 每一部分最大值
private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
// 每一部分向左的位移
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
private long datacenterId; //數據中心
private long machineId; //機器標識
private long sequence = 0L; //序列號
private long lastStmp = -1L; //上一次時間戳
public SnowFlake(long datacenterId, long machineId) {
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
}
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
}
this.datacenterId = datacenterId;
this.machineId = machineId;
}
//產生下一個ID
public synchronized long nextId() {
long currStmp = timeGen();
if (currStmp < lastStmp) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (currStmp == lastStmp) {
//if條件里表示當前調用和上一次調用落在了相同毫秒內,只能通過第三部分,序列號自增來判斷為唯一,所以+1.
sequence = (sequence + 1) & MAX_SEQUENCE;
//同一毫秒的序列數已經達到最大,只能等待下一個毫秒
if (sequence == 0L) {
currStmp = getNextMill();
}
} else {
//不同毫秒內,序列號置為0
//執行到這個分支的前提是currTimestamp > lastTimestamp,說明本次調用跟上次調用對比,已經不再同一個毫秒內了,這個時候序號可以重新回置0了。
sequence = 0L;
}
lastStmp = currStmp;
//就是用相對毫秒數、機器ID和自增序號拼接
return (currStmp - START_STMP) << TIMESTMP_LEFT //時間戳部分
| datacenterId << DATACENTER_LEFT //數據中心部分
| machineId << MACHINE_LEFT //機器標識部分
| sequence; //序列號部分
}
private long getNextMill() {
long mill = timeGen();
while (mill <= lastStmp) {
mill = timeGen();
}
return mill;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
當增加一秒生成ID的時候就是增加10位的機器標識+12位序列+約2的10次方(1000毫秒),最終就是增加一個2的32次方4 294 967 296就是42億左右
但是這里有一個坑,雪花算法產生的長整數的精度可能超過javascript能表達的精度,這會導致js獲取的id與雪花算法算出來的id不一致,如雪花算法得到的是36594866121080832,但是因為javascript丟失精度后只獲取到36594866121080830, 這會導致對數據的所有操作都失效。
解決辦法:后端的語言獲取到雪花算法的id后將其轉換為String類型,這樣js也會當做字符串來處理,就不會丟失精度了。
配置方法
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
public void configureMessageConverters(List> converters) {
converters.add(toStringConverter());
}
/**
* BigDecimal Long 轉化為String
*
* @return
*/
@Bean
public MappingJackson2HttpMessageConverter toStringConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(BigDecimal.class, BigDecimalToStringSerializer.instance);
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
simpleModule.addSerializer(long.class, ToStringSerializer.instance);
mapper.registerModule(simpleModule);
// Include.Include.ALWAYS 默認
// Include.NON_DEFAULT 屬性為默認值不序列化
// Include.NON_EMPTY 屬性為 空("") 或者為 NULL 都不序列化,則返回的json是沒有這個字段的。這樣對移動端會更省流量
// Include.NON_NULL 屬性為NULL 不序列化
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);// 允許出現特殊字符和轉義符
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); // 允許出現單引號
converter.setObjectMapper(mapper);
return converter;
}
@JacksonStdImpl
static class BigDecimalToStringSerializer extends ToStringSerializer {
public final static BigDecimalToStringSerializer instance = new BigDecimalToStringSerializer();
public BigDecimalToStringSerializer() {
super(Object.class);
}
public BigDecimalToStringSerializer(Class> handledType) {
super(handledType);
}
@Override
public boolean isEmpty(SerializerProvider prov, Object value) {
if (value == null) {
return true;
}
String str = ((BigDecimal) value).stripTrailingZeros().toPlainString();
return str.isEmpty();
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeString(((BigDecimal) value).stripTrailingZeros().toPlainString());
}
@Override
public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException {
return createSchemaNode("string", true);
}
@Override
public void serializeWithType(Object value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException {
// no type info, just regular serialization
serialize(value, gen, provider);
}
}
}
到此這篇關于Java實現雪花算法(snowflake)的文章就介紹到這了,更多相關Java 雪花算法內容請搜索我們以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持我們!
時間: 2020-08-25
總結
以上是生活随笔為你收集整理的java 有穷自动机_Java实现雪花算法(snowflake)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 千元机用户准备好 曝荣耀Play 7T即
- 下一篇: java单例代码_java中的单例模式的