
cn.basics.util.IdGZQ Maven / Gradle / Ivy
package cn.basics.util;
import java.util.concurrent.ConcurrentHashMap;
/**
* @ClassName: IdGZQ
* @Description: TODO(ID生成工具)
* @author [email protected] (苟志强)
* @date 2017-6-15 下午3:17:30
* 高并发分布式系统中生成全局唯一Id
* 优点:高性能,低延迟;独立的应用;按时间有序。
* 缺点:需要独立的开发和部署
*/
public class IdGZQ {
/*//循环生成ID 每次生成140万个ID 循环10次 查看效率
public static void main(String[] args) throws Exception {
long avg = 0;
for (int k = 0; k < 10; k++) {
List> partitions = new ArrayList>();
final IdGZQ idGen = IdGZQ.get();
for (int i = 0; i < 1400000; i++) {
partitions.add(new Callable() {
@Override
public Long call() throws Exception {
return idGen.nextId();
}
});
}
ExecutorService executorPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
try {
long s = System.currentTimeMillis();
executorPool.invokeAll(partitions, 10000, TimeUnit.SECONDS);
long s_avg = System.currentTimeMillis() - s;
avg += s_avg;
System.out.println("完成时间需要: " + s_avg / 1.0e3 + "秒");
executorPool.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("平均完成时间需要: " + avg / 10 / 1.0e3 + "秒");
}*/
public static void main(String[] args) {
long id = IdGZQ.getId();
System.out.println(id+":"+(id+"").length());
}
/**当前节点ID*/
private long workerId;
/**当前数据中心的身份ID*/
private long datacenterId;
/**序列号*/
private long sequence = 0L;
/**时间戳基值*/
private long twepoch = 1504646413000L;//1481836813311L
/**节点ID长度*/
private long workerIdBits = 5L;
/**数据中心ID长度*/
private long datacenterIdBits = 5L;
/**最大支持机器节点数0~31,一共32个*/
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
/**最大支持数据中心节点数0~31,一共32个*/
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
/**序列号12位*/
private long sequenceBits = 12L;
/**机器节点左移12位*/
private long workerIdShift = sequenceBits;
/**数据中心左移17位*/
private long datacenterIdShift = sequenceBits + workerIdBits;
/**时间毫秒数左移22位*/
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
/**序列的面具4095*/
private long sequenceMask = -1L ^ (-1L << sequenceBits);
/**上一次获取ID的时间戳*/
private long lastTimestamp = -1L;
/**
* @ClassName: IdGZQHolder
* @Description: TODO(IdGZQ对象生成器)
* @author [email protected] (苟志强)
* @date 2017-6-15 下午3:54:26
*/
private static class IdGZQHolder {
/**IdGZQ对象池 线程安全*/
private static final ConcurrentHashMap IDGZQS_MAP = new ConcurrentHashMap();
public static IdGZQ getGzq(long workerId, long datacenterId) {
IdGZQ idGZQ = IDGZQS_MAP.get(workerId+","+datacenterId);
if(idGZQ==null){
idGZQ = new IdGZQ(workerId, datacenterId);
}
return idGZQ;
}
}
/**
* @Title: get
* @Description: TODO(获取默认IdGZQ对象)
* @author [email protected] (苟志强)
* @date 2017-6-15 下午3:52:55
* @return
*/
public static IdGZQ get(){
return IdGZQHolder.getGzq(1L,1L);
}
public static IdGZQ get(long workerId, long datacenterId){
return IdGZQHolder.getGzq(workerId,datacenterId);
}
private IdGZQ() {
this(1L, 1L);
}
/**
* Title: 构造方法
* Description: 高并发分布式系统中生成全局唯一Id
* @author [email protected] (苟志强)
* @param workerId 节点ID 可为0L
* @param datacenterId 数据中心身份ID 可为0L
*/
private IdGZQ(long workerId, long datacenterId) {
//验证节点ID是否符合规则
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("节点ID不能大于%d或小于0", maxWorkerId));
}
//验证数据中心的身份ID是否符合规则
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("数据中心的身份ID不能大于%d或少于0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
IdGZQHolder.IDGZQS_MAP.put(workerId+","+datacenterId, this);
}
/**
* @Title: getId
* @Description: TODO(以默认对象获取ID)
* @author [email protected] (苟志强)
* @date 2017-6-30 下午5:18:21
* @return
*/
public synchronized static long getId(){
return IdGZQ.get().aNextId();
}
/**
* @Title: aNextId
* @Description: TODO(获取下一个ID<同步方法>,与nextId()方法一致!)
* @author [email protected] (苟志强)
* @date 2017-6-15 下午3:52:08
* @return
*/
public synchronized long aNextId() {
return nextId();
}
/**
* @Title: nextId
* @Description: TODO(获取下一个ID<同步方法>)
* @author [email protected] (苟志强)
* @date 2017-6-15 下午3:46:59
* @return
*/
public synchronized long nextId() {
//获取当前毫秒数
long timestamp = getNowTime();
//如果服务器时间有问题(时钟后退) 报错。
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("当前时钟向后移动(当前时间戳小于上一次获取ID的时间戳)拒绝生成小于%d毫秒的id!", lastTimestamp - timestamp));
}
//如果上次生成时间和当前时间相同,在同一毫秒内
if (lastTimestamp == timestamp) {
//sequence自增,因为sequence只有12bit,所以和sequenceMask相与一下,去掉高位
sequence = (sequence + 1) & sequenceMask;
//判断是否溢出,也就是每毫秒内超过4095,当为4096时,与sequenceMask相与,sequence就等于0
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp); //自旋等待到下一毫秒
}
} else {
sequence = 0L; //如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加
}
lastTimestamp = timestamp;
// 最后按照规则拼出ID。
// 000000000000000000000000000000000000000000 00000 00000 000000000000
// time datacenterId workerId sequence
long nextId = ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)| (workerId << workerIdShift) | sequence;
// System.out.println("获取到的ID:" + nextId + ",生成信息:" +"时间戳“" + timestamp + "”,时间戳基值“" + twepoch + "”,时间毫秒数左移位数“" + timestampLeftShift + "”,数据中心身份ID“" + datacenterId + "”,数据中心左移位数“" + datacenterIdShift + "”,节点ID“" + workerId + "”,节点左移位数“" + workerIdShift + "”,序列号“" + sequence +"”");
return nextId;
}
/**
* @Title: tilNextMillis
* @Description: TODO(等待下一毫秒)
* @author [email protected] (苟志强)
* @date 2017-6-15 下午3:50:16
* @param lastTimestamp
* @return
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = getNowTime();
while (timestamp <= lastTimestamp) {
timestamp = getNowTime();
}
return timestamp;
}
/**
* @Title: getNowTime
* @Description: TODO(获取当前时间戳)
* @author [email protected] (苟志强)
* @date 2017-6-15 下午3:50:47
* @return
*/
public long getNowTime() {
return System.currentTimeMillis();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy