返回相同数字的Java threaded Random.nextLong()
我使用的OAuth库调用new Random().nextLong()来生成nonce,但是它在异步调用时生成相同的nonce。我已经将其缩小为线程随机。nextLong()以每隔一段时间返回相同的精确数字 有人知道这是否是Java的已知限制吗?如果是,有人知道线程安全操作吗 编辑:我正在使用Java1.6 编辑:这是我制作的一个小程序,用来测试我更大的应用程序中发生了什么。我运行了好几次,而且更多的时候应该是这样的,当时间相同时,它会产生相同的随机数。请原谅我的快速编程返回相同数字的Java threaded Random.nextLong(),java,multithreading,random,thread-safety,Java,Multithreading,Random,Thread Safety,我使用的OAuth库调用new Random().nextLong()来生成nonce,但是它在异步调用时生成相同的nonce。我已经将其缩小为线程随机。nextLong()以每隔一段时间返回相同的精确数字 有人知道这是否是Java的已知限制吗?如果是,有人知道线程安全操作吗 编辑:我正在使用Java1.6 编辑:这是我制作的一个小程序,用来测试我更大的应用程序中发生了什么。我运行了好几次,而且更多的时候应该是这样的,当时间相同时,它会产生相同的随机数。请原谅我的快速编程 public clas
public class ThreadedRandom {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new ThreadedRandom().run();
}
private RandomNumberGenerator _generator;
public ThreadedRandom()
{
_generator = new RandomNumberGenerator();
}
public void run()
{
Runnable r = new Runnable() {
@Override public void run() {
System.out.println(System.currentTimeMillis()+"\t"+_generator.gen());
}
};
Thread t1, t2;
t1 = new Thread(r);
t2 = new Thread(r);
t1.start();
t2.start();
}
private class RandomNumberGenerator {
Random random;
public RandomNumberGenerator()
{
random = new Random();
}
public Long gen() {
return new Random().nextLong();
}
}
}您可能不希望每次都创建一个新的Random实例。随机数不是真正的随机数,它们是“伪随机”的,需要一个“种子”值。如果使用相同的种子,将生成相同的伪随机值序列 创建Random类的新实例时,可以自己指定种子,也可以让系统为您选择一个。在Java中,默认种子是以毫秒为单位的当前系统时间,请参阅: 如果在同一毫秒内创建随机对象,它们将具有相同的值序列
通常,您希望在所有不同的线程之间共享一个随机对象,以避免此类问题。虽然我对
Random
的底层实现不是很熟悉,但如果我不得不猜测的话,我会想象调用new Random()
将委托给new Random(System.currentTimeMillis())
。这为在不同时间实例化的随机数提供了合理不同的Random
序列
但是,由于您已经提到了异步调用,因此您的调用可能同时执行。这意味着当线程调用库时,它们都同时执行
new Random()
调用,并且随机数得到相同的种子,因此它们将生成相同的随机序列。我认为Random不是线程安全的,但它是。每2^48个值只能随机生成相同的数字
如前所述,Java1.4和previous有一个bug,两个随机数可以得到相同的种子。我建议您使用Java6,或者自己将种子设置为唯一的
您还应该注意,Random使用48位种子。这意味着它将在2^48个值之后重复,并且只生成2^48个唯一的长值。如果这对您来说是一个问题,请使用SecureRandom,它要贵得多,但会产生所有可能的长值。您确实应该使用SecureRandom,因为您正在使用它来处理与安全相关的事情。对于每次新对象,这也是不应该的。@org,希望每次都有新的Random。因为随机数是以当前毫秒为种子的,如果在同一毫秒内创建两个随机数,它们的数字将是相同的。错误。当前版本中的Random是通过
++seedUniquifier+System.nanoTime()
进行种子设定的,它应该以非常高的概率生成唯一的种子。@Justin:是的,这是我的观点。这就是seedUniquifier所做的,除了它不是从0开始,而是从某个任意数字开始。但是每次你创建一个Random
而不提供种子时,它就会增加。@Justin:1.5是它改变的时候。1.4具有您描述的行为。请参阅SecureRandom-这真的不应该发生,特别是不应该经常发生,因为它使用System.nanoTime和易失性seedUniquifier。您使用什么版本的Java运行代码?@Jigar-我添加了我的测试代码您的字段RandomNumberGenerator.random从未使用过,请注意编译器警告。对于我的Java 1.6,它工作得很好,时间是一样的,但长度是不同的。这对于任何仍在使用寿命内的Java版本都不是这样的,它是在6年前发布的:没错,没错,尽管我可能比其他人花更多的时间在旧的Java版本上。我很想知道OP使用的是什么版本。同样,它在Java1.4中使用过,但那是很久以前的事了。错了,Random是线程安全的。你认为他们为什么要使用volatile和AtomicLong?关于48位种子,你是对的。48位种子真的意味着它只生成2^48个唯一的长值,还是仅仅生成2^48个唯一的序列?显然,可能的第一组数字只能是2^48。但这并不意味着不能成为第一个数字的数字不能出现在序列的后面某个点上……例如,想象一个简单的“随机”生成,它的种子是1或0,算法是number=first?种子:编号+1
。尽管有一个1位种子,它仍然会访问每个数字。只是每个单独的调用只有2^1
可能的值。