仅仅为了思考练习,如何为给定类的每个实例强制执行属性的唯一性?
此处的唯一性可以定义为在单个JVM上和单个用户会话中。
这是Java级别,与数据库无关,主要目的是验证是否发生了冲突。
第一个明显的步骤是在类级别拥有静态属性。
如何解决这个问题?可能已存在哪些解决方案/方法?
答案 0 :(得分:4)
如果你关心性能,这里有一个线程安全,快速(无锁)和无碰撞版本的唯一id生成
public class Test {
private static AtomicInteger lastId = new AtomicInteger();
private int id;
public Test() {
id = lastId.incrementAndGet();
}
...
答案 1 :(得分:3)
只需使用Java http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html中的UUID
类。在检查的类中创建UUID类型的字段,并在构造函数中初始化此字段。
public class Test {
public UUID id;
public Test() {
id = UUID.randomUUID();
}
}
当检测到碰撞时,只需比较像这样的对象的UUID的字符串表示......
Test testObject1 = new Test();
Test testObject2 = new Test();
boolean collision = testObject1.id.toString().equals(testObject2.id.toString());
或者更简单地使用compareTo()
类中的UUID
方法...
boolean collision = testObject2.id.compareTo(testObject1.id) == 0 ? true : false;
0表示id
是相同的。当它们不相等时+1和-1。
优点:普遍独特(可以基于时间,随机),因此应该处理线程问题(有人应该确认这一点......这是基于我所知的最佳)。更多信息here和here。
要使其成为线程安全的,请参阅SO is java.util.UUID thread safe?
上的此问题缺点:将要求更改所检查的类的结构,即必须在类本身的源中添加id
字段。这可能是也可能不方便。
答案 2 :(得分:2)
UUID是一个很好的解决方案,但后端的UUID.randomUUID()使用方法:
synchronized public void SecureRandom.nextBytes(byte[] bytes)
所以这很慢:线程在每个id生成操作中锁定一个监视器对象。
AtomicInteger更好,因为它在CAS操作中循环。但同样,对于每个id生成操作,必须进行同步操作。
在下面的解决方案中,仅同步素数生成。同步在volatile上,因此快速且线程安全。拥有一组素数,在迭代中生成许多id。
编辑:固定线程数的解决方案
我知道有多少线程将使用Id生成,然后您可以使用值
生成IDId = I mod X + n*X
其中X是线程数,I是线程数,N是为每个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组。
要让线程保存Ids的生成,只需使用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))
}