web-dev-qa-db-ja.com

一意のIDのJavaのシーケンスジェネレーター

一意のIDを生成するためにポスト中にRESTリソース実装クラスで使用されるシーケンスジェネレーターを作成することを計画しています。すべてのポストリクエストは個別のスレッドによって処理されるため、変数を揮発性にし、メソッドが同期されました。従来のRDBMSが提供するシーケンスなどを使用するオプションがありません。

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

これは私がこれまでに持っているものであり、私のREST実装でSequenceGenの変数を作成することを計画しています。私の実際の質問はどこかで壊れますか?2つのスレッドでテストしましたが、値が表示されません繰り返された。

9
Ayan

動作しますが、AtomicIntegerはユースケースに最適な組み込み型です。

AtomicInteger seq = new AtomicInteger();
int nextVal = seq.incrementAndGet();
23
anttix

IDにStringの代わりに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()

UUIDintよりも優れたセキュリティを提供します。整数を使用すると、リクエストIDに1を追加するだけで次のリクエストIDを推測できますが、UUIDはシーケンシャルではなく、他の誰かのリクエストを推測する可能性がありますIDはかなりスリムです。

1
SergeyB

Java.util.prefs.Preferences シーケンスジェネレータの現在の状態をディスクに保持し、後で再び使用します。

(また、いくつかのシーケンスジェネレーターを使用することもできます)

つまり.

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
FaNaJ