【问题标题】:Why does Collections#shuffle not use ThreadLocalRandom?为什么 Collections#shuffle 不使用 ThreadLocalRandom?
【发布时间】:2018-11-20 03:38:56
【问题描述】:
public static void shuffle(List<?> list) {
    Random rnd = r;
    if (rnd == null)
        r = rnd = new Random(); // harmless race.
    shuffle(list, rnd);
}

private static Random r;

我注意到Collections#shuffle 使用new Random() 而不是ThreadLocalRandom.current(),我很好奇为什么。我写了一个比较两者的基准测试,使用ThreadLocalRandom.current() 的方法每次迭代都更快。这是基准:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 15, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 20, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(5)
public class MyBenchmark {
    @Param({"10", "1000", "100000"})
    private int i;

    private List<Integer> list;

    private static final Random RANDOM = new Random();

    @Setup
    public void initialize() {
        list = ThreadLocalRandom.current()
                                .ints(i)
                                .boxed()
                                .collect(Collectors.toList());
    }

    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }

    @Benchmark
    public List<Integer> oldMethod() {
        Collections.shuffle(list, RANDOM);
        return list;
    }

    @Benchmark
    public List<Integer> newMethod() {
        Collections.shuffle(list, ThreadLocalRandom.current());
        return list;
    }
}

结果:

Benchmark                 (i)  Mode  Cnt        Score       Error  Units
MyBenchmark.newMethod      10  avgt  100       60.802 ±     0.785  ns/op
MyBenchmark.newMethod    1000  avgt  100     6879.496 ±    15.638  ns/op
MyBenchmark.newMethod  100000  avgt  100  1248858.889 ± 33632.559  ns/op
MyBenchmark.oldMethod      10  avgt  100       90.636 ±     1.379  ns/op
MyBenchmark.oldMethod    1000  avgt  100     9740.442 ±    57.373  ns/op
MyBenchmark.oldMethod  100000  avgt  100  1511428.034 ± 27744.775  ns/op

那么,为什么new Random() 被使用而不是ThreadLocalRandom.current()

我的猜测:

  • 自 JDK 1.7 中 ThreadLocalRandom 发布以来,此方法尚未更新
  • new Random() 用于线程安全?

【问题讨论】:

  • 我猜这是你的第一个猜测。 Java 充满了可以(甚至应该)重新编写的遗留代码,但不是出于兼容性原因。
  • 最有可能? JDK 设计者不想在 Collections API 中构建基于ThreadLocal 的内存泄漏。
  • AFAICT Java 开发人员没有公开声明重写无错误代码以利用以后(也许更好)的 API 的政策。如果真的没有这样的公共政策,这真的是在征求人们对是否重写的意见,这对 SO 来说是题外话。

标签: java random collections shuffle


【解决方案1】:

Collections.shuffle(List)Collections.shuffle(List, Random) 方法是在 JDK 1.2 中添加的,而 ThreadLocalRandom 类直到 Java 7 才添加。因此,对于 Java 7 之前的所有版本,shuffle(List) 使用了Random.

虽然没有指定shuffle(List) 的随机性来源,但如果将其更改为使用不同的随机性来源,则可能是意外的行为变化。因此,该方法保持原样,使用Random 的实例。

没有太大必要改变它。其他随机性来源,例如 ThreadLocalRandomSecureRandom,是 Random 的子类,如果应用程序需要,它们可用于使用双参数重载 shuffle(List, Random) 进行洗牌。

【讨论】:

    猜你喜欢
    • 2012-08-29
    • 2011-01-23
    • 2017-03-29
    • 1970-01-01
    • 2012-06-23
    • 2014-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多