Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么一个线程的执行速度要比多个线程快得多?_Java_Multithreading_Concurrency_Executorservice - Fatal编程技术网

Java 为什么一个线程的执行速度要比多个线程快得多?

Java 为什么一个线程的执行速度要比多个线程快得多?,java,multithreading,concurrency,executorservice,Java,Multithreading,Concurrency,Executorservice,我在生产代码中遇到了这个问题,下面是一个简化的示例: public static void main(String[] args) throws ExecutionException, InterruptedException { long start = System.currentTimeMillis(); ExecutorService executor = Executors.newFixedThreadPool(1); // slower for > 1

我在生产代码中遇到了这个问题,下面是一个简化的示例:

public static void main(String[] args) throws ExecutionException, InterruptedException {

    long start = System.currentTimeMillis();

    ExecutorService executor = Executors.newFixedThreadPool(1); // slower for > 1
    List<CompletableFuture<Void>> futures = new ArrayList<>();

    for (int i = 0; i < 10; i++) {
        CompletableFuture<Void> future = new CompletableFuture<>();
        futures.add(future);
        executor.submit(() -> {
            int sum = 0; // prevent compiler to get rid of the loop
            for (int j = 0; j < 1_000; j++) {
                String random = RandomStringUtils.randomAlphanumeric(100, 10_000);
                sum += random.length();
            }
            System.out.println(Thread.currentThread().getName() + " sum: " + sum);
            future.complete(null);
        });
    }

    executor.shutdown();

    // prevent program to exit before branched threads complete
    for (CompletableFuture<Void> future : futures) {
        future.get();
    }

    System.out.println("Completed in: " +
            (System.currentTimeMillis() - start));
} 
10个线程:

pool-1-thread-6 sum: 4928768
pool-1-thread-10 sum: 4946490
pool-1-thread-5 sum: 4955353
pool-1-thread-8 sum: 5043251
pool-1-thread-3 sum: 5125496
pool-1-thread-4 sum: 5045113
pool-1-thread-2 sum: 5040489
pool-1-thread-1 sum: 5123954
pool-1-thread-9 sum: 5090715
pool-1-thread-7 sum: 5399434
Completed in: 11547
因此,使用10个线程,速度要慢10倍。两个线程的执行速度比一个线程慢约x1.5倍


我熟悉阿姆达尔定律。但我不确定,是这样吗?在我看来,这样的工作应该很容易并行化。

我怀疑它不能很好地扩展的原因在于Apache的代码

我发现RandomStringUtils使用标准的java.util.Random,因为这种代码,它不能很好地扩展多线程:

protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;
    do {
        oldseed = seed.get();
        nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
}
这使用原子长作为种子。换句话说,所有线程都使用相同的随机实例,它们使用相同的原子长度。这将导致线程之间的争用,特别是因为您正在生成如此长的随机字符串,它们将浪费大量周期来进行不必要的同步


当我用一种不同的CPU消耗函数(一个求和的循环)测试它时,多线程的伸缩效果与预期一样。

我怀疑它伸缩性不好的原因在于Apache的代码

我发现RandomStringUtils使用标准的java.util.Random,因为这种代码,它不能很好地扩展多线程:

protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;
    do {
        oldseed = seed.get();
        nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
}
这使用原子长作为种子。换句话说,所有线程都使用相同的随机实例,它们使用相同的原子长度。这将导致线程之间的争用,特别是因为您正在生成如此长的随机字符串,它们将浪费大量周期来进行不必要的同步


当我用一种不同的CPU消耗函数(一个求和的循环)测试它时,多线程的伸缩效果与预期一样。

它可以运行得更快,但需要使用多个线程,但使用的是固定大小的1。提高速度的唯一原因是代码在第二次运行时会变得更热,例如,其中一些代码会被预编译。如果您测试性能,您应该使用基准框架来测试代码。基准测试很难,而你完全错了。我熟悉JIT优化和微基准测试框架,比如JMH。正如我所说,这只是一个简化的例子,我们的生产代码在使用多示例线程时要慢10倍,如何解释?您的随机字符生成器将被同步。多线程的代码速度较慢,因为您正在使用争用资源,而使用争用资源的开销意味着N个线程的速度要慢N倍或更差。它可以运行得更快,但您需要使用多个线程,但您需要使用1的固定大小池。提高速度的唯一原因是代码在第二次运行时会变得更热,例如,其中一些代码会被预编译。如果您测试性能,您应该使用基准框架来测试代码。基准测试很难,而你完全错了。我熟悉JIT优化和微基准测试框架,比如JMH。正如我所说,这只是一个简化的例子,我们的生产代码在使用多示例线程时要慢10倍,如何解释?您的随机字符生成器将被同步。多线程的代码速度较慢,因为您使用的是争用资源,而使用争用资源的开销意味着N个线程的速度要慢N倍或更差。很好的发现:它们使用标准的java.util.random,它的文档明确提到了这一点:java.util.Random的实例是线程安全的。但是,跨线程并发使用相同的java.util.Random实例可能会遇到争用,从而导致性能低下。我将更新答案以反映这一点。谢谢!它有着完美的意义。很好的发现:它们使用标准的java.util.Random,其文档明确提到了这一点:java.util.Random的实例是线程安全的。但是,跨线程并发使用相同的java.util.Random实例可能会遇到争用,从而导致性能低下。我将更新答案以反映这一点。谢谢!这很有意义。