java - 意外的多线程结果

标签 java multithreading concurrency

我写了几个 Java 类——SingleThreadedComputeMultithreadedCompute——来证明一个事实(或者我一直认为是一个事实!)如果你并行计算-centric(无 I/O)单核机器上的任务,你不会得到加速。事实上,我的理解是并行化这些任务实际上会减慢速度,因为现在你必须处理上下文切换开销。好吧,我运行了这些类,并行版本出人意料地运行得更快:单线程版本在我的机器上始终以略高于 7 秒的速度运行,而多线程版本在我的机器上始终以略高于 6 秒的速度运行。谁能解释这是怎么可能的?

如果有人想自己查看或尝试,这里有类(class)。

public final class SingleThreadedCompute {
    private static final long _1B = 1000000000L; // one billion

    public static void main(String[] args) {
        long startMs = System.currentTimeMillis();

        long total = 0;
        for (long i = 0; i < _1B; i++) { total += i; }
        System.out.println("total=" + total);

        long elapsedMs = System.currentTimeMillis() - startMs;
        System.out.println("Elapsed time: " + elapsedMs + " ms");
    }
}

这是多线程版本:

public final class MultithreadedCompute {
    private static final long _1B = 1000000000L; // one billion
    private static final long _100M = _1B / 10L;

    public static void main(String[] args) {
        long startMs = System.currentTimeMillis();

        System.out.println("Creating workers");
        Worker[] workers = new Worker[10];
        for (int i = 0; i < 10; i++) {
            workers[i] = new Worker(i * _100M, (i+1) * _100M);
        }

        System.out.println("Starting workers");
        for (int i = 0; i < 10; i++) { workers[i].start(); }

        for (int i = 0; i < 10; i++) {
            try {
                workers[i].join();
                System.out.println("Joined with thread " + i);
            } catch (InterruptedException e) { /* can't happen */ }
        }

        System.out.println("Summing worker totals");
        long total = 0;
        for (int i = 0; i < 10; i++) { total += workers[i].getTotal(); }
        System.out.println("total=" + total);

        long elapsedMs = System.currentTimeMillis() - startMs;
        System.out.println("Elapsed time: " + elapsedMs + " ms");
    }

    private static class Worker extends Thread {
        private long start, end;
        private long total;

        public Worker(long start, long end) {
            this.start = start;
            this.end = end;
        }

        public void run() {
            System.out.println("Computing sum " + start + " + ... + (" + end + " - 1)");
            for (long i = start; i < end; i++) { total += i; }
        }

        public long getTotal() { return total; }
    }
}

这是运行单线程版本的输出:

total=499999999500000000
Elapsed time: 7031 ms

这是运行多线程版本的输出:

Creating workers
Starting workers
Computing sum 0 + ... + (100000000 - 1)
Computing sum 100000000 + ... + (200000000 - 1)
Computing sum 200000000 + ... + (300000000 - 1)
Computing sum 300000000 + ... + (400000000 - 1)
Computing sum 400000000 + ... + (500000000 - 1)
Computing sum 500000000 + ... + (600000000 - 1)
Computing sum 600000000 + ... + (700000000 - 1)
Computing sum 700000000 + ... + (800000000 - 1)
Computing sum 800000000 + ... + (900000000 - 1)
Computing sum 900000000 + ... + (1000000000 - 1)
Joined with thread 0
Joined with thread 1
Joined with thread 2
Joined with thread 3
Joined with thread 4
Joined with thread 5
Joined with thread 6
Joined with thread 7
Joined with thread 8
Joined with thread 9
Summing worker totals
total=499999999500000000
Elapsed time: 6172 ms

编辑:环境信息:

  • Microsoft Windows XP 专业版 2002,SP3
  • 戴尔 Precision 670
  • Intel Xeon CPU 2.80GHz,1 MB 二级缓存

不知道如何证明它是单核机器,除了通过说明上面的规范并指出当我购买机器时(2005 年 8 月),单核是标准的,我没有升级到多核(如果那甚至是一个选择……我不记得了)。如果 Windows 中的某个地方我可以检查系统属性(显示上面的信息)以外的内容,请告诉我,我会检查。


这里有五个连续的 ST 和 MT 运行:

五个单线程运行:

总计=499999999500000000 耗时:7000 毫秒

总计=499999999500000000 耗时:7031 毫秒

总计=499999999500000000 耗时:6922 毫秒

总计=499999999500000000 耗时:6968 毫秒

总计=499999999500000000 耗时:6938 毫秒


五个多线程运行:

总计=499999999500000000 耗时:6047 毫秒

总计=499999999500000000 耗时:6141 毫秒

总计=499999999500000000 耗时:6063 毫秒

总计=499999999500000000 耗时:6282 毫秒

总计=499999999500000000 耗时:6125 毫秒

最佳答案

这可能是由于超线程和/或流水线造成的。

来自维基百科 on hyper-threading :

Hyper-threading is an advancement over super-threading. Hyper-threading (officially termed Hyper-Threading Technology or HTT) is an Intel-proprietary technology used to improve parallelization of computations (doing multiple tasks at once) performed on PC microprocessors. A processor with hyper-threading enabled is treated by the operating system as two processors instead of one. This means that only one processor is physically present but the operating system sees two virtual processors, and shares the workload between them.

来自维基百科 on piplining :

In computing, a pipeline is a set of data processing elements connected in series, so that the output of one element is the input of the next one. The elements of a pipeline are often executed in parallel or in time-sliced fashion

关于java - 意外的多线程结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/519099/

相关文章:

multithreading - 原子的 Clojure 原子派生和重置

c - 在条件变量未发出信号时设置 settimer() 和 SIGALRM

c# - 非静态方法需要 objective-c #

go - 在双核 cpu 上运行 GO runtime.GOMAXPROCS(4)

java - 服务器机器中的不安全 WAR

java - 待澄清的简单多线程代码

java - 什么时候适合多线程?

java - 如何制作JButton图标透明,文字不透明?

java - 为什么为 Java VM 编写了这么多新语言?

java - 防止消息处理的竞争条件