UUID 是一个很好的解决方案,但是后端使用 UUID.randomUUID() 方法:
synchronized public void SecureRandom.nextBytes(byte[] bytes)
所以这很慢:线程在每个 id 生成操作中锁定一个监视器对象。
AtomicInteger 更好,因为它在 CAS 操作中循环。但同样,对于每个 id 生成操作,都必须进行同步操作。
在下面的解决方案中,只有质数生成是同步的。同步在 volatile int 上,因此速度快且线程安全。有一组素数,在一次迭代中会生成许多 id。
固定数量的线程
编辑:固定线程数的解决方案
我你知道有多少线程将使用 ID 生成,然后你可以生成带有值的 ID
Id = I mod X + n*X
其中 X 是线程数,I 是线程数,N 是一个局部变量,每次 Id 生成都会递增。这个解决方案的代码确实很简单,但它必须与hole program基础设施集成。
从素数生成的 ID
这个想法是生成 ids 作为素数的因子 id = p_1^f1 * p_2^f2 * p_2^f3 * ... * p_n^fn
我们在每个线程中使用不同的素数来在每个线程中生成不同的 id 集。
假设我们使用素数 (2,3,5),序列将是:
2, 2^2, 2^3, 2^4, 2^5,..., 2^64
然后,当我们看到将产生溢出时,我们将因子滚动到下一个素数:
3, 2*3 , 2^2*3, 2^3*3, 2^4*3, 2^5*3,..., 2^62*3
接下来
3^2, 2*3^2 , 2^2*3^2, .....
一代类
编辑:必须在 AtomicInteger 上完成引物订单生成才能正确
IdFactorialGenerator 类的每个实例都会生成不同的 id 集。
要让线程保存 Id 的生成,只需使用 ThreadLocal 来设置每个线程的实例。同步仅在质数生成期间实现。
package eu.pmsoft.sam.idgenerator;
public class IdFactorialGenerator {
private static AtomicInteger nextPrimeNumber = 0;
private int usedSlots;
private int[] primes = new int[64];
private int[] factors = new int[64];
private long id;
public IdFactorialGenerator(){
usedSlots = 1;
primes[0] = Sieve$.MODULE$.primeNumber(nextPrimeNumber.getAndAdd(1));
factors[0] = 1;
id = 1;
}
public long nextId(){
for (int factorToUpdate = 0; factorToUpdate < 64; factorToUpdate++) {
if(factorToUpdate == usedSlots) {
factors[factorToUpdate] = 1;
primes[factorToUpdate] = Sieve$.MODULE$.primeNumber(nextPrimeNumber.getAndAdd(1));
usedSlots++;
}
int primeToExtend = primes[factorToUpdate];
if( primeToExtend < Long.MAX_VALUE / id) {
// id * primeToExtend < Long.MAX_VALUE
factors[factorToUpdate] = factors[factorToUpdate]*primeToExtend;
id = id*primeToExtend;
return id;
} else {
factors[factorToUpdate] = 1;
id = 1;
for (int i = 0; i < usedSlots; i++) {
id = id*factors[i];
}
}
}
throw new IllegalStateException("I can not generate more ids");
}
}
为了获得质数,我使用问题 7 中提供的 scala 实现:http: //pavelfatin.com/scala-for-project-euler/
object Sieve {
def primeNumber(position: Int): Int = ps(position)
private lazy val ps: Stream[Int] = 2 #:: Stream.from(3).filter(i =>
ps.takeWhile(j => j * j <= i).forall(i % _ > 0))
}