为什么我的并发程序比顺序版慢?

Aja*_*rge 2 java multithreading

我试图对呼叫进行一些分析start,join同时,

    //Starting and Joining 
    for (Thread thread : threadArray) {
        thread.start();
        thread.join();
    }
Run Code Online (Sandbox Code Playgroud)

start第一次相比join.

    //Starting Them
    for (Thread thread : threadArray) {
        thread.start();
    }
    //Joining Them
    for (Thread thread : threadArray) {
        thread.join();
    }
Run Code Online (Sandbox Code Playgroud)

上述两种情况之间的性能差异是什么?

在第一个场景中,我几乎可以保证线程之间的执行顺序是顺序的.因此,如果我有n线程并说每个线程需要Ti时间来完成任务,那么我的总执行时间应该是Tis从1到n的总和.

在第二种情况下,我开始然后加入.这是我感到困惑的部分.难道时间不应该与上面几乎相同吗?我所看到的几乎是我机器的两倍.

我正在使用的整个代码示例如下所示.

public class ThreadJoin implements Runnable {

    public void run() {
        for (int i=0;i<10000000;i++) {
            //Random mathematical stuff independent of i.
             int ran = (int) (Math.random()*1000 -34)%47;
        }
    }

    public static void main(String[] args) throws Exception {
        Thread[] threadArray = new Thread[10];
        //Creating threads and feeding them with the job
        for (int i=0;i<10;i++) {
            threadArray[i] = new Thread(new ThreadJoin());
        }
        long currentTimeMillis = System.currentTimeMillis();
        System.out.println("Started at " + currentTimeMillis);
        //Starting Them
        for (Thread thread : threadArray) {
            thread.start();
        }
        //Joining Them
        for (Thread thread : threadArray) {
            thread.join();
        }
        long currentTimeMillis2 = System.currentTimeMillis();
        System.out.println("Ended at " + currentTimeMillis2);
        System.out.println("Diff : " +( currentTimeMillis2 - currentTimeMillis));
    }

}
Run Code Online (Sandbox Code Playgroud)

Paŭ*_*ann 7

从理论上讲,首先启动所有线程然后加入它们都应该允许你的10个线程同时执行(即同时执行),而在一个循环中启动和连接线程将使它们并行运行.

因此,从理论上讲,双环变体应该更快.为什么它实际上更慢(如果我理解这一点)?

Math.random()在你的循环中非常重要.事实上,我认为大部分工作都发生在这种方法中.Math.random()是一个同步方法 - 这意味着一次只能有一个线程执行它,而其他线程必须等到前一个完成.

所以,你不能真正比这里顺序更快.它实际上变慢了,因为你的许多线程之间有很多上下文切换,然后大多数线程会发现它们无法继续,因为另一个线程已经有了锁.

为了使程序更快,让每个线程都有自己的java.util.Random()对象,并调用它的nextRandom()方法.(你可能想确保用不同的种子初始化它们.)

正如Tomek的评论中提到的,从Java 7开始就有一个ThreadLocalRandom类,它为每个线程组织一个Random对象池,并通过其current()方法为当前线程公开一个.(我从来没有使用过这个,所以与手动操作相比,我无法评论性能.)


x22*_*x22 5

第二个版本(启动所有线程,然后加入所有线程)允许线程并行运行,因此它应该在多处理器或多核机器上更快(更短的总时间).但Math.random()是同步的,它可以使它实际上更慢.

从Math.random()的文档:

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