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
这个想法是生成 id 作为素数的因子
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))
}