我正在尝试使用生成随机值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();
编辑:
另一方面,如果我避免使用流,而只是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()
”。
PS:当然,我可以解决此问题,但同步线程以获取下一个值。
虽然SecureRandom
它本身是线程安全的,但流不是。整个Streams API的构建都可以通过单个线程进行访问。尽管可以并行执行中间操作,但必须从单个线程中调用它们。
因此ints()
,它的迭代器也不是线程安全的。
因此,你可以做的是为每个线程创建一个流。
Thread t1 = new Thread(() -> secureRandom.ints().distinct().iterator().next());
Thread t2 = new Thread(() -> secureRandom.ints().distinct().iterator().next());
t1.start();
t2.start();
感谢您的回答。了解有关线程安全如何与流(尤其是与)一起工作的更多知识将很有趣
iterator()
。但是,提出的解决方案不是很合适,因为其目的是确保不同的值(即,即使极不可能,也不会重复生成任何值)。@MAnouti我解释了流的线程安全性。您还想知道什么?为了确保多个线程具有不同的值,您将需要实现解决方案(可能使用并发哈希集)。
在这里,我对
iterator()
操作特别感兴趣,因为与其他终端操作不同,它似乎不评估整个流,这似乎很特殊。该ints()
方法的文档指出“伪随机int值好像是调用该方法的结果nextInt()
。” 现在,如果我仅使用nextInt()
,就不会发生竞争状况。因此,只是想知道为什么iterator()
会有不同的行为(我已经更新了问题以明确说明)。如果提到流通常不是线程安全的事实(例如官方文档或讨论),我可以接受答案。尽管仍然感觉它正在破坏的线程安全性保证
SecureRandom
。@MAnouti我认为线程安全类可以具有非线程安全的衍生类。的合同
Iterator
未要求其必须是线程安全的。流的迭代器的行为会有所不同,因为它是从流生成的,并且流不是线程安全的。