All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.norconex.commons.lang.TimeIdGenerator Maven / Gradle / Ivy

Go to download

Norconex Commons Lang is a Java library containing utility classes that complements the Java API and are not found in commonly available libraries (such as the great Apache Commons Lang, which it relies on).

There is a newer version: 2.0.2
Show newest version
/* Copyright 2015 Norconex Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.norconex.commons.lang;

import java.math.BigDecimal;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

/**
 * 

* Generates a unique ID made out of the current time in milliseconds, * combined with a thread-safe atomic sequence value that guarantees * order and uniqueness within the same JVM. *

*

* This class was created for cases where unique sequential IDs are required * to be {@code long} primitives, * or where {@code long} are desired for things such as faster lookup * and generation (over large strings for instance) and you can't use a * persistent sequence generator. * If you do not have such need, it is best advised to use a * universal id generator instead, like java {@link UUID}. *

* *

The fine-prints

*

* At a minimum, values generated by this * class ensures uniqueness within JVM instances (read * Year 2262 and beyond further down). * If you need larger or different values to ensure greater * uniqueness between many JVMs, * you can convert the returned value to a string and simply * prefix it with a value of your choice (or convert to a {@code BigInteger} * if your can support larger numbers). * To obtain universally unique IDs, consider using a UUID implementation, * like the Java {@link UUID}. *

*

* IDs generated within a single JVM are guaranteed to be in order with no * duplicates (read * Year 2262 and beyond further down). * The sequence usually has many gaps. *

*

* Generated IDs are {@code long} primitives. Java {@code long} values * are 64-bit whereas standard UUID are byte arrays of 128-bit * (plus the array reference itself). When using UUIDs in their common * hexadecimal string format, they usually are at least 576-bit (plus the * string reference). *

*

* The reduced byte size of {@code long} values compared to most strings * may lead to lookup performance, in * addition of some system sorting primitive numbers faster than strings. * While not especially optimized for ID generation, creating {@code long}-based * IDs is much faster than most string-based approach. For instance, this class * is typically more than 20 times faster at generating {@code long} values * than Java UUID at generating strings. * An average desktop computer can show it takes less than 50 milliseconds to * generate 1 million IDs (single thread). *

*

* Be advised this implementation does not account for the possibility of * a backward UTC time change on the host system clock (as opposed to * "local" time which can change without issues as long as UTC time is * unaffected). *

*

* Assuming your application and underlying platform can * achieve this feat, a maximum of 1 million unique IDs can be generated every * milliseconds (1 billion IDs per seconds). Every time that threshold is * reached, the method will wait until the current time has progressed * to the next millisecond to prevent ID duplication (waiting 1 nanosecond * at a time). *

*

* Java {@code long} values can hold 19 characters. Each digit is part * of one of two groups of digits with a specific purpose. * The exact pattern is the following: *

*
 * Java long max value:   9223372036854775808
 * ------------------------------------------
 * Current time (ms):     9223372036854          <--- max value
 * Atomic sequence:                    999999    <--- max value
 * 
*

* This class is thread-safe. *

* * *

Year 2262 and beyond

*

* Whenever the millisecond EPOCH representation of the current time reaches * 1e14, the time representation starts back at 0ms using a modulo between * the current time and 1e14. The first time this will occur is when the * system UTC clock time reaches April 11th, 2262. The moment * rollback of the current time value occurs, new IDs will be smaller * {@code long} values than IDs generated prior to that date. * If you only care about uniqueness, your IDs will still be unique unless * you have been generating IDs using this class for close to 300 years or more. *

* * @author Pascal Essiembre * @since 1.6.0 */ public final class TimeIdGenerator { private static final Logger LOG = LogManager.getLogger(TimeIdGenerator.class); private static final AtomicInteger DUP_SEQUENCE = new AtomicInteger(); private static final long MILLIS_ROLLOVER_VALUE = BigDecimal.valueOf(1e14).longValueExact(); private static final int MAX_DUP_SEQUENCE = 999999; private static final int TIME_MULTIPLIER = 1000000; private static long previousTime = -1; private static long previousGeneratedId = -1; private static int previousGeneratedDupSequence = 0; private TimeIdGenerator() { super(); } /** * Returns the last generated number since the start of this * JVM (that value is not persisted anywhere outside the JVM memory). * Invoking this method before having called {@link #next()} at least * once will return {@code -1}. * @return the last id generated */ public synchronized static long last() { return previousGeneratedId; } /** * Generates a new number unique within this JVM. * @return a long value */ public synchronized static long next() { long time = System.currentTimeMillis() % MILLIS_ROLLOVER_VALUE; long id = time * TIME_MULTIPLIER; id += getDupSequence(time == previousTime); previousTime = time; previousGeneratedId = id; return id; } private static int getDupSequence(boolean needsIncrement) { if (needsIncrement) { int dupSeq = DUP_SEQUENCE.incrementAndGet(); previousGeneratedDupSequence = dupSeq; if (dupSeq == MAX_DUP_SEQUENCE) { while (System.currentTimeMillis() == previousTime) { Sleeper.sleepNanos(1); } if (LOG.isDebugEnabled()) { LOG.debug("Reached " + (MAX_DUP_SEQUENCE + 1) + " ID creations in one millisecond. " + "Had to wait for current millisecond to pass."); } } return dupSeq; } else { if (previousGeneratedDupSequence != 0) { DUP_SEQUENCE.set(0); previousGeneratedDupSequence = 0; } return 0; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy