1
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?

请解释..

4

2 回答 2

3

Java 7 文档写道:

的实例java.util.Random是线程安全的。但是,跨线程并发使用同一java.util.Random实例可能会遇到争用,从而导致性能下降。考虑改为ThreadLocalRandom在多线程设计中使用。

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

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

也就是说,您的代码还有许多其他问题。就像上面引用的那样,性能可能很差。正如assylias 已经在评论中写的那样,这种方法不会比简单的方法给你更多的唯一数字Random。此外,Math.abs(Long.MIN_VALUE)仍然是负数,正数随机数加上 id 可能会导致溢出和回绕。因此,如果您需要正数,则必须更加小心。决赛uniqueID &= 0x7fffffffffffffffL可能比Math.abs一路走来更合适。

于 2013-02-06T14:04:02.580 回答
1

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

正如@assylias 所指出的,它甚至可能不会在单线程环境中产生唯一的 ID。

值得研究一下ObjectIdMongoDB的生成机制。

引入了四个参数以确保唯一性。

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

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

需要注意的是,在 JDK 7 之前它不是线程安全的。

于 2013-02-06T14:04:48.797 回答