SecureRandom 流具有多个线程的奇怪行为
SecureRandom stream weird behavior with multiple threads
我正在尝试使用 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[=11=](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.SpliteratorsAdapter.hasNext(Spliterators.java:726)
at java.base/java.util.SpliteratorsAdapter.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()
".
P.S.: 当然我可以解决这个问题但是同步获取下一个值的线程。
虽然 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();
我正在尝试使用 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[=11=](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.SpliteratorsAdapter.hasNext(Spliterators.java:726)
at java.base/java.util.SpliteratorsAdapter.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()
".
P.S.: 当然我可以解决这个问题但是同步获取下一个值的线程。
虽然 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();