Java 在并行流#过滤器中使用随机数有效吗?
问题是获取一个输入集合,随机过滤它(例如,以50%的概率包含所有元素,否则跳过它们)。这可以通过以下方式使用常规for循环完成:Java 在并行流#过滤器中使用随机数有效吗?,java,random,java-stream,Java,Random,Java Stream,问题是获取一个输入集合,随机过滤它(例如,以50%的概率包含所有元素,否则跳过它们)。这可以通过以下方式使用常规for循环完成: Random random = new Random(); List<Object> list; // suppose that this list is populated with some elements List<Object> filteredList = new ArrayList<>(); for (int i
Random random = new Random();
List<Object> list; // suppose that this list is populated with some elements
List<Object> filteredList = new ArrayList<>();
for (int i = 0; i < list.size(); ++i) {
if (random.nextDouble() < 0.5) {
filteredList.add(list.get(i));
}
}
是首选的方式。还是我遗漏了什么要点
EDIT:如果通过
filter
方法中的ThreadLocalRandom#current
获取随机实例是正确的方法,那么如果我事先实例化它并使用相同的实例(如第一个代码示例所示),会发生什么?从不同线程同时调用nextDouble
是否会返回相同的数字?来自文档:
java.util.Random
的实例是线程安全的。然而,并发的
跨线程使用相同的java.util.Random
实例可能会遇到
争用和随之而来的性能差。考虑使用
多线程设计中的ThreadLocalRandom
伪随机性中断的唯一情况是使用同一种子在不同线程上初始化不同的随机源(例如,使用当前时间作为种子,然后基于同一种子创建不同的ThreadLocalRandom
s)
编辑:对于在不同线程中使用相同的
ThreadLocalRandom
,基本上,ThreadLocalRandom
使用了一些魔法来确保它从当前线程获得种子(请参见nextSeed()
的实现) 请注意,parallel
要产生任何影响,您的源代码列表必须非常庞大。是的,这也应该考虑在内。这更像是一个一般的行为问题,而不是我遇到的问题。然而,我认为使用的线程数比列表的大小更相关,因为这是导致并发访问的原因。。。为元素不多的列表添加parallel
,只会由于线程调度等原因引入延迟,并且会得到比非并行更慢的结果。通过在筛选器调用之前分配实例,可以完全禁用ThreadLocalRandom
的功能ThreadLocalRandom.current()
为每个线程返回一个新对象,并且只有在并行调用期间,它才会被不同的线程调用。因此,您的建议是在筛选器方法中实例化ThreadLocalRandom
(即在执行指令的线程中),就像我作为最后一个代码示例编写的那样?这应该是可以的,除非System.nanoTime()
以某种方式为两个ThreadLocalRandom
初始化返回相同的值,这将导致重复的种子。风险很低,但并非不可能。如果您真的担心,请使用java.util.secureRandomSeed
属性。@PiotrWilkin我认为System.nanoTime
不可能为两个不同的调用返回相同的值。谢谢您的评论,这听起来很合理,也是我所期望的。但是,我还想知道不在filter方法中实例化random的风险是什么-调用nextDouble
会导致不同线程的数字相同吗?我要回答这个问题。
Random random = new Random();
List<Object> list;
List<Object> filteredList = list.stream()
.filter(element -> random.nextDouble() < 0.5)
.collect(Collectors.toList());
.filter(element -> ThreadLocalRandom.current().nextDouble() < 0.5)