Java sequence generator for unique identifier

I plan to write a sequence generator that will be used in the REST resource implementation class at the time of publication to generate a unique identifier. Since each post request is processed by a separate thread, I made a volatile variable and the method is synchronized. I don’t have the ability to use sequences or something that is a traditional DBMS.

public class SequenceGen { volatile static int n = 0; public synchronized int nextNum(){ return n++; } } 

this is what I still have and plan to create a SequenceGen variable in my REST implementation. My actual question is will it break something? I tested two threads, and I do not see any value being repeated.

+8
java multithreading sequence
source share
3 answers

It will work, however AtomicInteger is a built-in type that is ideal for your use.

 AtomicInteger seq = new AtomicInteger(); int nextVal = seq.incrementAndGet(); 
+20
source share

If you use String for identifiers rather than int , you may need to examine the UUID (universal unique identifier). Very easy to use and, as the name suggests, they are unique. Here is an example of how to generate one:

 // the value of uuid will be something like '03c9a439-fba6-41e1-a18a-4c542c12e6a8' String uuid = java.util.UUID.randomUUID().toString() 

A UUID also provides better security than int , because with integers you can guess the next request identifier by simply adding 1 to your request identifier, but the UUIDs are not consecutive, and it is likely that someone will guess that someone still asks for an identifier, thin.

+1
source share

You can use java.util.prefs.Preferences to save the current state of the sequence generator to disk and use it later.

(also you can use several sequence generators)

i.e.

 import java.lang.ref.SoftReference; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.prefs.Preferences; public final class SequenceGenerator { private static final Preferences PREFS = Preferences.userNodeForPackage(SequenceGenerator.class); private static final AtomicLong SEQ_ID = new AtomicLong(Integer.parseInt(PREFS.get("seq_id", "1"))); private static final Map<Long, SoftReference<SequenceGenerator>> GENERATORS = new ConcurrentHashMap<>(); private static final SequenceGenerator DEF_GENERATOR = new SequenceGenerator(0L, Long.parseLong(PREFS.get("seq_0", "1"))); static { Runtime.getRuntime().addShutdownHook(new Thread(() -> { GENERATORS.values().stream() .map(SoftReference::get) .filter(seq -> seq != null && seq.isPersistOnExit()) .forEach(SequenceGenerator::persist); if (DEF_GENERATOR.isPersistOnExit()) { DEF_GENERATOR.persist(); } PREFS.put("seq_id", SEQ_ID.toString()); })); } private final long sequenceId; private final AtomicLong counter; private final AtomicBoolean persistOnExit = new AtomicBoolean(); private SequenceGenerator(long sequenceId, long initialValue) { this.sequenceId = sequenceId; counter = new AtomicLong(initialValue); } public long nextId() { return counter.getAndIncrement(); } public long currentId() { return counter.get(); } public long getSequenceId() { return sequenceId; } public boolean isPersistOnExit() { return persistOnExit.get(); } public void setPersistOnExit(boolean persistOnExit) { this.persistOnExit.set(persistOnExit); } public void persist() { PREFS.put("seq_" + sequenceId, counter.toString()); } @Override protected void finalize() throws Throwable { super.finalize(); GENERATORS.remove(sequenceId); if (persistOnExit.get()) { persist(); } } @Override public int hashCode() { return Long.hashCode(sequenceId); } @Override public boolean equals(Object obj) { return obj == this || obj != null && obj instanceof SequenceGenerator && sequenceId == ((SequenceGenerator) obj).sequenceId; } @Override public String toString() { return "{" + "counter=" + counter + ", seq=" + sequenceId + '}'; } public static SequenceGenerator getDefault() { return DEF_GENERATOR; } public static SequenceGenerator get(long sequenceId) { if (sequenceId < 0) { throw new IllegalArgumentException("(sequenceId = " + sequenceId + ") < 0"); } if (sequenceId == 0) { return DEF_GENERATOR; } SoftReference<SequenceGenerator> r = GENERATORS.computeIfAbsent(sequenceId, sid -> { try { return new SoftReference<>(new SequenceGenerator(sid, Long.parseLong(PREFS.get("seq_" + sid, null)))); } catch (Throwable t) { return null; } }); return r == null ? null : r.get(); } public static SequenceGenerator create() { return create(1); } public static SequenceGenerator create(long initialValue) { long sequenceId = SEQ_ID.getAndIncrement(); SequenceGenerator seq = new SequenceGenerator(sequenceId, Long.parseLong(PREFS.get("seq_" + sequenceId, "" + initialValue))); GENERATORS.put(sequenceId, new SoftReference<>(seq)); return seq; } } 
+1
source share

All Articles