ULID规范解读与实现原理( 四 )


public static ULID ulid() {return ulid(System::currentTimeMillis, len -> {byte[] bytes = new byte[len];ThreadLocalRandom.current().nextBytes(bytes);return bytes;});}public static ULID ulid(Supplier<Long> timestampProvider,IntFunction<byte[]> randomnessProvider) {return new ULID(timestampProvider.get(), randomnessProvider.apply(RANDOMNESS_BYTE_LEN));}默认使用ThreadLocalRandom生成随机数,如果是JDK17以上,还可以选用更高性能的新型PRNG实现,对应接口是RandomGenerator,默认实现是L32X64MixRandom 。编写一个main方法运行一下:
public static void main(String[] args) {System.out.println(ULID.ulid());}// 某次执行结果01GFGGMBFGB5WKXBN7S84ATRDG最后实现"单调递增"的ULID构造,先提供一个"增长"方法:
/*** The least significant 64 bits increase overflow, 0xffffffffffffffffL + 1*/private static final long OVERFLOW = 0x0000000000000000L;public ULID increment() {long msb = this.msb;long lsb = this.lsb + 1;if (lsb == OVERFLOW) {msb += 1;}return new ULID(msb, lsb);}其实就是低位加1,溢出后高位加1 。接着添加一个静态内部子类和响应方法如下:
// 构造函数public ULID(ULID other) {this.msb = other.msb;this.lsb = other.lsb;}public static byte[] defaultRandomBytes(int len) {byte[] bytes = new byte[len];ThreadLocalRandom.current().nextBytes(bytes);return bytes;}public static MonotonicULIDSpi monotonicUlid() {return monotonicUlid(System::currentTimeMillis, ULID::defaultRandomBytes);}public static MonotonicULIDSpi monotonicUlid(Supplier<Long> timestampProvider,IntFunction<byte[]> randomnessProvider) {return new MonotonicULID(timestampProvider, randomnessProvider, timestampProvider.get(),randomnessProvider.apply(RANDOMNESS_BYTE_LEN));}// @SPI MonotonicULIDpublic interface MonotonicULIDSpi {ULID next();}private static class MonotonicULID extends ULID implements MonotonicULIDSpi {@Serialprivate static final long serialVersionUID = -9158161806889605101L;private volatile ULID lastULID;private final Supplier<Long> timestampProvider;private final IntFunction<byte[]> randomnessProvider;public MonotonicULID(Supplier<Long> timestampProvider,IntFunction<byte[]> randomnessProvider,long timestamp,byte[] randomness) {super(timestamp, randomness);this.timestampProvider = timestampProvider;this.randomnessProvider = randomnessProvider;this.lastULID = new ULID(timestamp, randomness);}// 这里没设计好,子类缓存了上一个节点,需要重写一下increment方法,父类可以移除此方法@Overridepublic ULID increment() {long newMsb = lastULID.msb;long newLsb = lastULID.lsb + 1;if (newLsb == OVERFLOW) {newMsb += 1;}return new ULID(newMsb, newLsb);}@Overridepublic synchronized ULID next() {long lastTimestamp = lastULID.getTimestamp();long timestamp = getTimestamp();// 这里做了一个恒为true的判断 , 后面再研读其他代码进行修改if (lastTimestamp >= timestamp || timestamp - lastTimestamp <= 1000) {this.lastULID = this.increment();} else {this.lastULID = new ULID(timestampProvider.get(), randomnessProvider.apply(RANDOMNESS_BYTE_LEN));}return new ULID(this.lastULID);}}关于上一个ULID和下一个ULID之间的时间戳判断,这里从规范文件没看出细节实现,先简单做一个永远为true的分支判断,后面再深入研究然后修改 。使用方式如下:
public static void main(String[] args) {MonotonicULIDSpi spi = ULID.monotonicUlid();System.out.println(spi.next());System.out.println(spi.next());System.out.println(spi.next());System.out.println(spi.next());}// 某次运行输出01GFGASXXQXD5ZJ26PKSCFGNPF01GFGASXXQXD5ZJ26PKSCFGNPG01GFGASXXQXD5ZJ26PKSCFGNPH01GFGASXXQXD5ZJ26PKSCFGNPJ这里为了更加灵活 , 没有采用全局静态属性缓存上一个ULID实例 , 而是简单使用继承方式实现 。
ULID性能评估引入JMH做了一个简单的性能测试 , 代码如下:
@Fork(1)@Threads(10)@State(Scope.Benchmark)@BenchmarkMode(Mode.Throughput)@Warmup(iterations = 1, time = 1)@Measurement(iterations = 5, time = 3)@OutputTimeUnit(TimeUnit.MILLISECONDS)public class BenchmarkRunner {private static ULID.MonotonicULIDSpi SPI;@Setuppublic void setup() {SPI = ULID.monotonicUlid();}@Benchmarkpublic UUID createUUID() {return UUID.randomUUID();}@Benchmarkpublic String createUUIDToString() {return UUID.randomUUID().toString();}@Benchmarkpublic ULID createULID() {return ULID.ulid();}@Benchmarkpublic String createULIDToString() {return ULID.ulid().toString();}@Benchmarkpublic String createULIDToCanonicalString0() {return ULID.ulid().toCanonicalString0();}@Benchmarkpublic ULID createMonotonicULID() {return SPI.next();}@Benchmarkpublic String createMonotonicULIDToString() {return SPI.next().toString();}public static void main(String[] args) throws Exception {new Runner(new OptionsBuilder().build()).run();}}

推荐阅读