使用atomic生成唯一id

时间:2013-02-06 12:38:50

标签: java atomic uniqueidentifier java.util.concurrent

class ABC{
private static Random random = new Random();
private static AtomicLong uniqueLongId = new AtomicLong(System.currentTimeMillis());

public static long getUniqueLongId(){

            long id = uniqueLongId.incrementAndGet();
            long uniqueID = Math.abs(random.nextLong()) + id;
            return uniqueID;

            //the above code we can write in one line
    //return Math.abs(random.nextLong())+uniqueLongId.incrementAndGet();

}
}

以上方法getUniqueLongId()是否会在多线程环境中为我提供唯一ID。我关注的是:知道uniqueLongId是原子的并且假设调用incrementAndGet()将是一个线程安全的调用,但代码的另一部分不同步。这不意味着方法getUniqueLongId()本身不是线程安全的吗?因此可能无法返回唯一ID?

请解释..

2 个答案:

答案 0 :(得分:2)

Java 7 docs写道:

  

java.util.Random的实例是线程安全的。但是,跨线程同时使用相同的java.util.Random实例可能会遇到争用并因此导致性能不佳。请考虑在多线程设计中使用ThreadLocalRandom

因此,您的代码在Java 7中是线程安全的。每个操作都是对线程安全方法的调用,或者仅对局部变量进行操作。并且您不需要原子性,即您不需要将下一个序列号与下一个随机数配对。

As(根据您的评论)在旧版本的API文档中没有这样的保证,理论上实现可能是非线程安全的。但是,查看Sun JDK 1.4.2.19中的src.zip(这是我最老的版本),该代码已经使用了原子变量,从而在实践中提供了线程安全的行为。

也就是说,您的代码还存在许多其他问题。如上所述,表现可能不好。作为assylias already wrote in a comment,此方法不会为您提供比简单Random更多的唯一数字。此外,Math.abs(Long.MIN_VALUE)仍然是负数,正的随机数加上一个id可能会导致溢出和环绕。因此,如果您需要正数,则必须增加更多关注。最后uniqueID &= 0x7fffffffffffffffL可能比Math.abs更合适。

答案 1 :(得分:1)

确实是线程安全的。 MvG的答案解释了原因。

如@assylias所述,它甚至可能无法在单线程环境中生成唯一ID。

在研究MongoDB的ObjectId生成机制时,这是值得的。

为了确保独特性,引入了四种参数。

a 4-byte timestamp,
a 3-byte machine identifier,
a 2-byte process id, and
a 3-byte counter.

话虽如此,JDK中有一个UUID类,可以用于此目的。

caveat因为它在JDK 7之前不是线程安全的。