繁体   English   中英

基本Java线程(4个线程)比非线程慢

[英]Basic Java threading (4 threads) slower than non-threading

我有一个四核CPU。 我创建了4个线程并运行了一个cpu密集型循环,它比在一个线程中以程序方式运行所需的时间长4倍。

我创建了两个要比较的项目,一个是线程,另一个没有。 我将展示代码和运行时间。 请注意没有线程的项目看起来很奇怪的原因是我想复制内存开销,因为我不确定它会影响运行时间。 所以,这是没有线程的代码:

class TimeTest implements Runnable {
    private Thread t;
    private String name;

    TimeTest(String name) {
        this.name = name;
        System.out.println("Creating class " + name);
    }

    public void run() {
        System.out.println("Running class " + name);

        int value = 100000000;
//        try {
            while (--value > 0) {
                Math.random();
//                Thread.sleep(1);
//                System.out.println("Class " + name + " " + value);
            }
//        } catch (InterruptedException e) {
//            System.out.println("Interrupted " + name);
//        }

        System.out.println("Class " + name + " exiting...");
    }

    public void start() {
        System.out.println("Starting class " + name);

        if (t == null) {
            t = new Thread(this, name);
//            t.start();
            this.run();
        }
    }
}

public class ThreadComp {
    public static void main(String[] args) {
        TimeTest one = new TimeTest("Class-1");
        one.start();

        TimeTest two = new TimeTest("Class-2");
        two.start();

        TimeTest three = new TimeTest("Class-3");
        three.start();

        TimeTest four = new TimeTest("Class-4");
        four.start();
    }
}

这运行大约11秒。

这是带线程的代码:

class RunnableTest implements Runnable {
    private Thread t;
    private String name;

    RunnableTest(String name) {
        this.name = name;
        System.out.println("Creating thread " + name);
    }

    public void run() {
        System.out.println("Running thread " + name);
        int value = 100000000;
//        try {
            while (--value > 0) {
                Math.random();
//                Thread.sleep(1);
//                System.out.println("Thread " + name + " " + value);
            }
//        } catch (InterruptedException e) {
//            System.out.println("Interrupted " + name);
//        }

        System.out.println("Thread " + name + " exiting...");
    }

    public void start() {
        System.out.println("Starting thread " + name);

        if (t == null) {
            t = new Thread(this, name);
            t.start();
        }
    }
}

public class ThreadTest {
    public static void main(String[] args) {
        RunnableTest one = new RunnableTest("Thread-1");
        one.start();

        RunnableTest two = new RunnableTest("Thread-2");
        two.start();

        RunnableTest three = new RunnableTest("Thread-3");
        three.start();

        RunnableTest four = new RunnableTest("Thread-4");
        four.start();
    }    
}

这运行大约1分13秒。

现在,在我正在学习的例子中,他们在领导期间调用Thread.sleep达50ms。 如果我这样做,线程运行得更快如果我也在非线程类上调用Thread.sleep(50)。

哪个好,我知道如何让它发挥作用。 但是我学习这个的原因是我正在寻找路径,而且我不打算在已经需要很长时间的事情上添加Sleep调用,并且不需要暂停甚至不执行1ms(除非它绝对必须)。

那么,我想知道的是我错过了什么? 线程绝对必须要进入休眠状态,或者对象是否必须等待它们才能按照我的意图工作(即并行运行所有四个循环)?

即使我只是犯了一个错误,为什么这会花费更长的时间? 我认为最糟糕的情况是,它仍然会在11秒内运行,它会以一些不可预见的顺序完成....

执行时间的巨大差异是由Math.random()方法引起的。 如果您将深入了解它的实现,您将看到它使用在所有线程之间共享的静态randomNumberGenerator 如果你更进一步,那么你会注意到执行依赖于int next(int)方法,而后者又使用Random.seed ,即AtomicLong (考虑所有线程使用相同的Random实例!)。 现在我们来到AtomicLong ,这是通过乐观锁定实现的 - 这就是问题所在。 乐观锁不是为高负载而设计的,当多个线程试图同时访问它们时,它们会受到很大的影响,这就是您所观察到的性能下降。

TL; DR:使用ThreadLocalRandom (感谢@ bayou.io提到这一点)并享受性能提升。

你的问题是你正在使用Math.random() 关于这种方法的文档:

...

此方法已正确同步,以允许多个线程正确使用。 但是,如果许多线程需要以很高的速率生成伪随机数,则可以减少每个线程拥有自己的伪随机数生成器的争用。

(强调我的)

所以解决方案是为每个线程创建一个新的Random

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM