Почему два потока Java (в некоторых случаях) более чем в два раза быстрее одного? - PullRequest
2 голосов
/ 19 сентября 2009

Файл: Example1.java

public class Example1 implements Runnable {

    public void run() {
        for(int i = 0; i < 100000000; i++) {
            int x = 5;
            x = x * 4;
            x = x % 3;
            x = x + 9000;
            x = x * 923;
        }
    }

    public static void task() {
        for(int i = 0; i < 100000000; i++) {
            int x = 5;
            x = x * 4;
            x = x % 3;
            x = x + 9000;
            x = x * 923;
        }
        for(int i = 0; i < 100000000; i++) {
            int x = 9;
            x = x * 2;
            x = x % 4;
            x = x + 3241;
            x = x * 472;
        }
    }

    public static void main(String[] args) {

        long startTime = System.currentTimeMillis();
            Example1.task();
            Example1.task();
            Example1.task();
            Example1.task();
            Example1.task();
        long stopTime = System.currentTimeMillis();
        long runTime = stopTime - startTime;
        System.out.println("Run time for one thread: " + runTime);


        startTime = System.Example1();
            (new Thread(new Example1())).start();
            (new Thread(new Example2())).start();
            (new Thread(new Example1())).start();
            (new Thread(new Example2())).start();
            (new Thread(new Example1())).start();
            (new Thread(new Example2())).start();
            (new Thread(new Example1())).start();
            (new Thread(new Example2())).start();
            (new Thread(new Example1())).start();
            (new Thread(new Example2())).start();
        stopTime = System.currentTimeMillis();
        runTime = stopTime - startTime;
        System.out.println("Run time for two threads: " + runTime);


    }

}

Файл: Example2.java

public class Example2 implements Runnable {

    public void run() {
        for(int i = 0; i < 100000000; i++) {
            int x = 9;
            x = x * 2;
            x = x % 4;
            x = x + 3241;
            x = x * 472;
        }        
    }
}

Когда я запускаю это, выводит:

Время выполнения для одной нити: 1219

Время выполнения для двух потоков: 281

или что-то очень близкое.

Почему такая разница? Почему разделение его на два потока происходит более чем в два раза быстрее, чем простой запуск?

Ответы [ 3 ]

19 голосов
/ 19 сентября 2009

Вы на самом деле не ждете окончания потоков вообще.

Как только вы запустите поток, вы должны затем вызвать .join (), чтобы дождаться его завершения. Здесь происходит то, что все ваши потоки запускаются, и как только запускается последний, вы синхронизируете его, а затем вычисляете время остановки. Это означает, что ваши потоки все еще работают в фоновом режиме.

Редактировать: Причина, по которой первый занимает так много времени, заключается в том, что вы делаете серию синхронных вызовов, создавая поток и запуская его, порождая асинхронную задачу.

Редактировать 2: Вот диаграмма последовательности салфеток того, что происходит в вашем первом тесте: http://www.websequencediagrams.com/cgi-bin/cdraw?lz=TWFpbi0-RXhhbXBsZTE6IFRhc2sgc3RhcnRlZAphY3RpdmF0ZSAAGAgKACEILS0-TWFpbjogZG9uZQpkZQAYEgABWAABWAABgTFlMQo&s=napkin

Вот диаграмма последовательности салфеток того, что происходит во втором тесте: http://www.websequencediagrams.com/cgi-bin/cdraw?lz=TWFpbi0tPkFub255bW91cyBUaHJlYWQ6IFN0YXJ0IEV4YW1wbGUxLnRhc2soKQoACSYyAAEuAAFdAAGBOwCCPjoAgyIGPk1haW46ICJIb3cgbG9uZyBkaWQgdGhhdCB0YWtlPyIKAINmEC0AKwhUYXNrcyBiZWdpbiB0byBmaW5pc2guLi4gKHNvbWUgbWF5IGhhdmUgZW5kZWQgZWFybGllcikK&s=napkin

Редактировать 3: Я только что понял, что вторая диаграмма последовательности указывает все стрелки на / тот же / поток. Они на самом деле разные темы, каждый вызов.

2 голосов
/ 19 сентября 2009

Вызов start () в потоке немедленно возвращается, потому что он просто ставит в очередь поток. Сам поток начнет работать в фоновом режиме через некоторое время.

1 голос
/ 19 сентября 2009

Вот что я получаю с вашим кодом, добавляя присоединение к потокам:

Время выполнения для одной нити: 566

Время выполнения для двух потоков: 294

Так что предыдущие ответы верны.

РЕДАКТИРОВАТЬ: я добавил присоединения таким образом. Вы можете сделать это лучше, но это не имеет значения:

    Thread[] t = new Thread[10];
    (t[0] = new Thread(new Example1())).start();
    (t[1] = new Thread(new Example2())).start();
    (t[2] = new Thread(new Example1())).start();
    (t[3] = new Thread(new Example2())).start();
    (t[4] = new Thread(new Example1())).start();
    (t[5] = new Thread(new Example2())).start();
    (t[6] = new Thread(new Example1())).start();
    (t[7] = new Thread(new Example2())).start();
    (t[8] = new Thread(new Example1())).start();
    (t[9] = new Thread(new Example2())).start();

    for (Thread t1: t) {
        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

Вы должны присоединиться к каждой теме. Однако вы не тратите время на ожидание в join (), потому что другие потоки не блокируются. Если поток завершил свою работу перед вызовом присоединения, вы просто переходите к следующему потоку.

Кроме того, что означает ваш последний комментарий?

...