Java 中用于唯一 ID 的序列生成器
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22416826/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sequence Generator in Java for Unique Id
提问by Ayan
I am planning to write a sequence generator which will be used in my REST resource implementation class during post to generate unique id. Since every post request is handled by separate thread, I made the variable volatile and method synchronized. I don't have option to use sequences or something which traditional RDBMS provides.
我计划编写一个序列生成器,它将在发布期间用于我的 REST 资源实现类中以生成唯一 id。由于每个 post 请求都由单独的线程处理,因此我使变量 volatile 和方法同步。我没有选择使用序列或传统 RDBMS 提供的东西。
public class SequenceGen {
volatile static int n = 0;
public synchronized int nextNum(){
return n++;
}
}
this is what I have so far, and planning to create a variable of SequenceGen in my REST Implementation. My actual question is will it break somewhere ? I tested with two threads and I dont see any value repeated.
这就是我到目前为止所拥有的,并计划在我的 REST 实现中创建一个 SequenceGen 变量。我的实际问题是它会在某个地方破裂吗?我用两个线程进行了测试,但没有看到任何重复的值。
回答by anttix
It will work, however AtomicInteger
is an built in type that is perfect for your use case.
它会起作用,但是它AtomicInteger
是一种非常适合您的用例的内置类型。
AtomicInteger seq = new AtomicInteger();
int nextVal = seq.incrementAndGet();
回答by SergeyB
If you are open to using String
for IDs, instead of int
, you might want to look into using UUID
(Universally Unique Identifier). Very easy to use and as the name implies, they are unique. Here is an example of how to generate one:
如果您愿意使用String
for ID 而不是int
,您可能需要考虑使用UUID
(Universally Unique Identifier)。非常易于使用,顾名思义,它们是独一无二的。以下是如何生成一个的示例:
// 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 ID by simply adding 1 to your request ID, but UUIDs are not sequential and chances of anyone guessing anyone else's request ID are pretty slim.
AUUID
还提供了比int
使用整数更好的安全性,因为您可以通过简单地将 1 添加到您的请求 ID 来猜测下一个请求 ID,但 UUID 不是连续的,任何人猜测其他人的请求 ID 的机会都非常渺茫。
回答by FaNaJ
You can take advantage of java.util.prefs.Preferences
to persist the current state of your sequence generator on the disk and use it again later.
您可以利用java.util.prefs.Preferences
将序列生成器的当前状态保留在磁盘上并在以后再次使用它。
(also, you may want to use several sequence generators)
(另外,您可能想要使用多个序列生成器)
i.e.
IE
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;
}
}