java无锁算法_java实现雪花算法,不使用synchronized,无锁实现

用雪花算法生成唯一id,使用java原子库,无锁算法实现。不说废话,直接上代码

package com.sudytech.orm2.surpport.util.seq;

import java.util.Date;

import java.util.concurrent.atomic.AtomicStampedReference;

/**

* 雪花算法生成唯一id,线程安全,使用元字库,无同步锁实现

*

 
 

* 0-00000000000000000000000000000000000000000-0000000000-000000000000

* x-yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy-zzzzzzzzzz-dddddddddddd

* (x) 1bit,不用,二进制中最高位是符号位,1表示负数,0表示正数。生成的id一般都是用整数,所以最高位固定为0。

*

* (y) 41bit-时间戳,用来记录时间戳,毫秒级。 - 41位最大值,换算成"年"最大可以表示约69年

*

* (z) 10bit-工作机器id,用来记录工作机器id。 - 可以部署在个多个节点,包括5位datacenterId和5位workerId

* 5位(bit)可以表示的最大正整数是,即可以用0、1、2、3、....31这32个数字,来表示不同的datecenterId或workerId

*

* (d) 12bit-序列号,序列号,用来记录同毫秒内产生的不同id。 -

* 12位(bit)可以表示的最大正整数是,即可以用0、1、2、3、....4094这4095个数字,来表示同一机器同一时间截(毫秒)内产生的4095个ID序号。

*

*/

public class SnowFlower {

// 最大序列,12位

private static final int MAX_SEQUENCE = (1 << 12) - 1;

// 起始时间, 2016-01-01

private static final long START_TIME = 1451577600000L;

//最大时间戳, 41位

private static final long MAX_TIME = (1L << 41) -1;

// 带时间的序列,原子操作

private AtomicStampedReference current;

// 数据中心id

private long datacenterId;

// 工作id

private long workerId;

//提前计算好的机器id

private long machineId;

public SnowFlower(int datacenterId, int workerId) {

StampedSequence dt = new StampedSequence(START_TIME, 0);

current = new AtomicStampedReference(dt, 0);

int maxId = (1 << 5) -1;

if(datacenterId < 0 || datacenterId > maxId) {

throw new IllegalArgumentException(

String.format("datacenterId can't be greater than %d or less than 0", maxId));

}

if(workerId < 0 || workerId > maxId) {

throw new IllegalArgumentException(

String.format("workerId can't be greater than %d or less than 0", maxId));

}

this.datacenterId = datacenterId;

this.workerId = workerId;

//初始化机器id

machineId = ((this.datacenterId << 5) | this.workerId) << 12;

}

public long nextId() {

StampedSequence seq = nextSequence();

long time = seq.timestamp - START_TIME;

if( time > MAX_TIME) {

throw new RuntimeException("Timestamp overflow! " + new Date(seq.timestamp));

}

long value = (time << 22) | machineId | seq.sequence;

return value;

}

StampedSequence nextSequence() {

//下一个时间戳序列

StampedSequence nextSequence = new StampedSequence(0, 0);

//版本

int[] versionHolder = new int[1];

while (true) {

long now = System.currentTimeMillis();

StampedSequence curSequence = current.get(versionHolder);

if (now < curSequence.timestamp) {

throw new RuntimeException("Clock moved backwards!");

}else if (curSequence.timestamp == now) {

if (curSequence.sequence >= MAX_SEQUENCE) {

//满序列等待下一毫秒

continue;

}

nextSequence.timestamp = curSequence.timestamp;

nextSequence.sequence = curSequence.sequence + 1;

boolean set = current.compareAndSet(curSequence, nextSequence, versionHolder[0], versionHolder[0] + 1);

if (!set) {

// 无锁更新失败,重新获取

continue;

}

break;

} else {

nextSequence.timestamp = now;

nextSequence.sequence = 0;

boolean set = current.compareAndSet(curSequence, nextSequence, versionHolder[0], versionHolder[0] + 1);

if (!set) {

// 无锁更新失败,重新获取

continue;

}

break;

}

}

return nextSequence;

}

/**

* 带时间戳的序列

*/

static class StampedSequence {

private long timestamp; // 时间戳

private long sequence; // 序列

public StampedSequence(long stamp, long sequence) {

super();

this.timestamp = stamp;

this.sequence = sequence;

}

}

/* 测试 */

public static void main(String[] args) {

SnowFlower snow = new SnowFlower(1, 1);

for (int i = 0; i < 300; i++) {

long seq = snow.nextId();

System.out.println(Long.toString(seq, 2));

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
雪花算法是一个生成分布式ID的算法,它的核心思想是将时间戳、机器ID、序列号组合而成。下面是一个使用Java实现雪花算法的示例代码: ```java public class SnowflakeIdGenerator { //开始时间戳,用于减少ID长度 private final static long START_TIMESTAMP = 1577808000000L; //2020-01-01 00:00:00 //每一部分占用的位数 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_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); private final static long MAX_MACHINE = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_DATACENTER = -1L ^ (-1L << DATACENTER_BIT); //每一部分向左的位移 private final static long MACHINE_LEFT = SEQUENCE_BIT; private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; private final static long TIMESTAMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT; //数据中心ID private long datacenterId; //机器ID private long machineId; //序列号 private long sequence = 0L; //上一次生成ID的时间戳 private long lastTimestamp = -1L; public SnowflakeIdGenerator(long datacenterId, long machineId) { if (datacenterId > MAX_DATACENTER || datacenterId < 0) { throw new IllegalArgumentException("Datacenter ID can't be greater than " + MAX_DATACENTER + " or less than 0"); } if (machineId > MAX_MACHINE || machineId < 0) { throw new IllegalArgumentException("Machine ID can't be greater than " + MAX_MACHINE + " or less than 0"); } this.datacenterId = datacenterId; this.machineId = machineId; } public synchronized long nextId() { long timestamp = timeGen(); if (timestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id"); } if (timestamp == lastTimestamp) { sequence = (sequence + 1) & MAX_SEQUENCE; if (sequence == 0L) { timestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - START_TIMESTAMP) << TIMESTAMP_LEFT) | (datacenterId << DATACENTER_LEFT) | (machineId << MACHINE_LEFT) | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } } ``` 使用示例: ```java SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1); long id = idGenerator.nextId(); System.out.println(id); ``` 输出结果: ``` 1070035452723917824 ``` 注意,这个示例代码只是一个简单的实现,实际使用中需要根据具体情况进行修改和优化。例如,可以将机器ID和数据中心ID从配置文件中读取,以便在不同的环境中使用不同的ID生成器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值