我正在尝试使用SecureRandom
生成随机值,特别是它对流的支持。理想情况下,这些值应该被持续地生成,所以流可以是无限的:
SecureRandom secureRandom = new SecureRandom();
Iterator<Integer> idIterator = secureRandom.ints().distinct().iterator();
文档说明:“
SecureRandom
对象可被多个并发线程安全使用。然而,当多个线程从迭代器中检索下一个值时,我会在(至少)一个线程中收到错误信息,这似乎是由于竞争条件导致的:Thread t1 = new Thread(() -> idIterator.next());
Thread t2 = new Thread(() -> idIterator.next());
t1.start();
t2.start();
Exception in thread "Thread-1" java.lang.IllegalStateException: source already consumed or closed
at java.base/java.util.stream.AbstractPipeline.sourceSpliterator(AbstractPipeline.java:409)
at java.base/java.util.stream.AbstractPipeline.lambda$spliterator$0(AbstractPipeline.java:367)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.init(StreamSpliterators.java:142)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.doAdvance(StreamSpliterators.java:157)
at java.base/java.util.stream.StreamSpliterators$IntWrappingSpliterator.tryAdvance(StreamSpliterators.java:358)
at java.base/java.util.Spliterators$2Adapter.hasNext(Spliterators.java:726)
at java.base/java.util.Spliterators$2Adapter.nextInt(Spliterators.java:732)
at java.base/java.util.PrimitiveIterator$OfInt.next(PrimitiveIterator.java:128)
at java.base/java.util.PrimitiveIterator$OfInt.next(PrimitiveIterator.java:86)
at example.Example.foo(Example.java:39)
当我运行代码多次时,有时会出现另一种异常(NullPointerException)。
如果我限制流并删除distinct()操作,行为是相同的:
secureRandom.ints().limit(100).iterator();
编辑:
另一方面,如果我避免使用流(stream)并且只是在每个线程中调用SecureRandom.nextInt()
,则如预期那样不会观察到竞争条件。
Thread t1 = new Thread(() -> secureRandom.nextInt());
Thread t2 = new Thread(() -> secureRandom.nextInt());
t1.start();
t2.start(); // code is thread-safe
我想知道迭代器为什么会改变行为?特别是在ints()
方法的Javadocs中说明了,"生成一个伪随机int值,就像调用nextInt()
方法一样"。
P.S.:当然,我可以通过同步获取下一个值的线程来解决这个问题。
iterator()
方面会很有趣。然而,提出的解决方案并不是非常适合,因为意图是确保不同的值(即使这很不可能重复生成任何值)。 - M Aiterator()
操作,它似乎很特殊,因为它不像其他终端操作一样评估整个流。ints()
方法的文档说明“伪随机int值就像调用nextInt()
方法的结果一样。”现在,如果我只是使用nextInt()
,就不会发生竞争条件。所以我想知道为什么iterator()
的行为会有所不同(我已经更新了问题以澄清)。 - M ASecureRandom
的线程安全保证。 - M AIterator
的契约并不要求它是线程安全的。流的迭代器会表现出不同的行为,因为它是从流中生成的,而流不是线程安全的。 - spongebob