Почему один поток быстрее, чем многопоточность в Java?

Насколько я понимаю, я написал простую однопользовательскую и многопоточную программу ниже, чтобы проверить скорость выполнения. Но моя однопоточная программа выполняется быстрее, чем многопоточность, любезно см. Ниже программу и упоминает, что что-то не так.

Одиночная тема:

import java.util.Calendar;

public class NormalJava {
    public static void main(String[] args) {
        System.out.println("Single Thread");
        int a = 1000;
        int b = 200;
        NormalJava nj = new NormalJava();
        nj.Add(a, b);
        nj.Sub(a, b);
        nj.Mul(a, b);
        nj.Div(a, b);
        Calendar lCDateTime = Calendar.getInstance();
        System.out.println("Calender - Time in milliseconds :"
                + lCDateTime.getTimeInMillis());

    }

    private void Add(int a, int b) {
        System.out.println("Add :::" + (a + b));
    }

    private void Sub(int a, int b) {
        System.out.println("Sub :::" + (a - b));
    }

    private void Mul(int a, int b) {
        System.out.println("Mul :::" + (a * b));
    }

    private void Div(int a, int b) {
        System.out.println("Mul :::" + (a / b));
    }
}

Вывод:
  Одиночная тема |   Добавить: 1200
  Sub: 800
  Mul: 200000
  Mul: 5
  Calender - Время в миллисекундах: 138 415 866 7863


Многопоточная программа:

package runnableandcallable;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class MainThread {

    private static ExecutorService service = Executors.newFixedThreadPool(10); // connection
                                                                               // pool
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Multithreading");
        MainThread mt = new MainThread();
        mt.testThread(1000, 200);
        Calendar lCDateTime = Calendar.getInstance();
        System.out.println("Calender - Time in milliseconds :"
                + lCDateTime.getTimeInMillis());
    }

    public void testThread(final int a, final int b) {
        // create a callable for each method
        Callable<Void> callableAdd = new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                Add(a, b);
                return null;
            }
        };

        Callable<Void> callableSub = new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                Sub(a, b);
                return null;
            }
        };

        Callable<Void> callableMul = new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                Mul(a, b);
                return null;
            }
        };

        Callable<Void> callableDiv = new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                Div(a, b);
                return null;
            }
        };

        // add to a list
        List<Callable<Void>> taskList = new ArrayList<Callable<Void>>();
        taskList.add(callableAdd);
        taskList.add(callableSub);
        taskList.add(callableMul);
        taskList.add(callableDiv);

        // create a pool executor with 3 threads
        ExecutorService executor = Executors.newFixedThreadPool(3);

        try {
            // start the threads
            List<Future<Void>> futureList = executor.invokeAll(taskList);

            for (Future<Void> voidFuture : futureList) {
                try {
                    // check the status of each future. get will block until the
                    // task
                    // completes or the time expires
                    voidFuture.get(100, TimeUnit.MILLISECONDS);
                } catch (ExecutionException e) {
                    System.err
                            .println("Error executing task " + e.getMessage());
                } catch (TimeoutException e) {
                    System.err.println("Timed out executing task"
                            + e.getMessage());
                }

            }

        } catch (InterruptedException ie) {
            // do something if you care about interruption;
        }

    }

    private void Add(int a, int b) {
        System.out.println("Add :::" + (a + b));
    }

    private void Sub(int a, int b) {
        System.out.println("Sub :::" + (a - b));
    }

    private void Mul(int a, int b) {
        System.out.println("Multiply :::" + (a * b));
    }

    private void Div(int a, int b) {
        System.out.println("Division :::" + (a / b));
    }

}

Малитреющий вывод:
  Многопоточность
  Sub: 800
  Отдел: 5
  Добавить: 1200
  Multiply: 200000
  Calender - Время в миллисекундах: 138 415 868 0821

Здесь один поток выполняется на 138 415 866 7863 миллисекундах и многопоточность, выполненная на этом этапе 138 415 868 0821 миллисекунды. Тогда какова истинная цель многопоточности?

Ответ 1

Обработка, которую вы делаете, тривиальна, поэтому накладные расходы на создание потоков дороже.

Если у вас были дорогостоящие операции, которые можно было выполнить параллельно, то много потоков имеют смысл.

Ответ 2

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

Второй: Для создания микро-бенчмарка вы должны использовать JMH

Ответ 3

1,384,158,667,863 миллисекунды составляют около 44 лет. Итак, вы говорите нам, что вы ожидали 44 года от результата этой операции? Или может быть что-то не так, как вы измеряете скорость выполнения?

Чтобы измерить разницу в два раза, вам нужно как минимум два раза, в то время как вы получаете только текущую дату в конце вашей программы, которая даже не близка к точной.

Простой класс измерения времени:

public class StopWatch {
  private long startTime = -1;

  public void start() {
    this.startTime = System.nanoTime();
  }

  public long timeNanos() {
    return System.nanoTime() - this.startTime;
  }

  public double timeMillis() {
    return this.timeNanos() / 1000000.0;
  }
}

Используйте этот секундомер, чтобы измерить время выполнения (например, использовать секундомер), затем сделайте это 3 раза и поймите, что каждый раз, когда вы получаете совершенно разные результаты. Это связано с тем, что измерение точного времени выполнения вообще не является тривиальным. ОС постоянно прерывает выполнение вашей программы другими задачами, и, казалось бы, простые команды могут иметь целую цепочку фоновых команд, которые необходимо запустить.

Все, что вы можете сделать, это приблизительное время, затрачиваемое на выполнение этой задачи, как миллион раз, а затем принять среднее значение.

Ответ 4

Прежде всего, ваше время в миллисекундах - это только метка времени. Вам нужна разница в милисекундах между до и после вызова, чтобы измерить прошедшее время. Я предполагаю, что сначала вы запускали однопоточное приложение. Если вы попытаетесь запустить многопоточное приложение сначала, вы заметите, что оно имеет более низкое значение "время в миллисекундах".

Во-вторых. Создание и управление потоками имеет накладные расходы, что намного выше, чем время выполнения самых простых арифметических операций, которые вы выполняете. Если вы попытаетесь выполнить итерацию операций несколько миллионов раз, вы можете увидеть прирост производительности, выполнив параллельные операции.

Ответ 5

Если вы рассматриваете один процессор. Все потоки выполняются на одном процессоре. предположим, что ваша программа (jvm) имеет время выполнения на процессоре каждую секунду. Если вы выполняете в одном потоке, 0,2 секунды будут выделены только для этого основного потока. Если вы выполните его на 4 потоках, например, 0,2 секунды, вы не будете иметь 0,05 + 0,05 + 0,05 + 0,05. Вам нужно будет добавить дополнительное время для синхронизации, возобновления и удаления потоков. Если мы предположим, что эта операция занимает 0.001 секунды для каждого переключения контекста. Вы получаете 0,004 секунды времени выполнения, теряемого каждую секунду, при условии, что поток выполняется один раз в секунду. В реальной жизни переключение контекста потока выполняется много раз в секунду, и оно непредсказуемо. Теперь все меняется, так как многоядерные машины и потоки могут выполняться одновременно на разных ядрах.

Для получения дополнительной информации см. эту ссылку: Поддерживает ли Java многоядерные процессоры/параллельную обработку?