Java параллельные запросы - PullRequest
1 голос
/ 09 марта 2011

Я строю тестирование приложений на Java в качестве эксперимента. Цель этого инструмента - выяснить, насколько быстро работает конкретная база данных (например, Derby, MySQL).

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

Я думал о создании нескольких потоков, где каждый поток выполняет несколько запросов. Но в данный момент запросы, похоже, выполняются после завершения другого запроса, а не одновременно.

У меня есть следующий (упрощенный) код:

Runner testCase = new Runner();

for (int i = 0; i < THREAD_COUNT; i++) {
    Thread testThread = new Thread(testCase);
    testThread.start();
}

public class Runner implements Runnable {

    public void run() {

      for (int i = 0; i < Benchmarker.QUERY_COUNT; i++) {

        stopwatch2.start();
        List<Person> selectData = dataHandler.selectData();
        stopwatch2.stop();
        counter += stopwatch2.getStopwatchValue();

       }
    }
}

У процессора моей тестовой системы есть два ядра, поэтому должна быть возможность запуска двух потоков одновременно, верно?

Кто-нибудь знает, как реализовать эту опцию?

Спасибо за ваше время и помощь!

ОБНОВЛЕНИЕ - добавлен код метода selectData :

public List<Person> selectData() {

List<Person> data = new ArrayList<Person>();

try {
    // Select all persons from the database
    ResultSet resultSet = connection.prepareStatement("SELECT * FROM PERSON ORDER BY name").executeQuery();

    // Add all the persons to a arraylist
    while (resultSet.next()) {
    data.add(new Person(resultSet.getString("name")));
    }

    // Close the resultset
    resultSet.close();

} catch (SQLException ex) {
    Logger.getLogger(Derby.class.getName()).log(Level.SEVERE, null, ex);
}

return data;
}

Ответы [ 3 ]

4 голосов
/ 09 марта 2011

Здесь есть две проблемы:

  1. Вы должны дождаться окончания всех тем. Это можно сделать вручную, но гораздо проще не использовать Threads, а ExecutorService , который предлагает вам такие методы, как invokeAll() или awaitTermination(), которые делают это для вас. Чтобы получить ExecutorService, используйте методы из класса Executors. Этот класс также предлагает методы для переноса Runnable в Callable. Таким образом, в методе main вы должны создать ExecutorService, передать все runnables в цикле for, вызвать shutdown() и awaitTermination(). Затем выведите значение счетчика.
  2. Вы должны позаботиться о правильном добавлении времени. Для этого важно, чтобы каждый экземпляр Runnable использовал свой собственный секундомер, поэтому переменная stopwatch2 должна быть локальной переменной run(). Кроме того, переменная counter не может быть нормальной длинной, но она должна быть AtomicLong . В противном случае времена некоторых потоков могут быть потеряны, поскольку обычное добавление не является атомарной операцией (два потока могут одновременно попытаться добавить свое время к переменной counter, что, вероятно, приведет к неверному результату).

Вот код:

void runTests() {
  Runner testCase = new Runner();
  ExecutorService executor = Executors.newCachedThreadPool();

  for (int i = 0; i < THREAD_COUNT; i++) {
    executor.execute(testCase);
  }
  executor.shutdown();
  executor.awaitTermination(60, TimeUnit.SECONDS);
  System.out.println(counter.toString());
}

private AtomicLong counter = new AtomicLong();

public class Runner implements Runnable {

    public void run() {
      StopWatch stopwatch2 = ... // get a stopwatch instance here

      for (int i = 0; i < Benchmarker.QUERY_COUNT; i++) {

        stopwatch2.start(); // this needs to reset the stopwatch to 0
        List<Person> selectData = dataHandler.selectData();
        stopwatch2.stop();
        counter.addAndGet(stopwatch2.getStopwatchValue());

       }
    }
}
3 голосов
/ 09 марта 2011

Если вы используете одно и то же соединение SQL между потоками, это может быть вашей проблемой. В общем, вам следует избегать совместного использования одного и того же соединения между разными потоками и использовать вместо этого пулы соединений.

0 голосов
/ 09 марта 2011

Наиболее вероятная причина описанного вами поведения заключается в том, что dataHandler.selectData() либо synchronized, либо использует метод synchronized.

Чтобы решить эту проблему, вам нужно либо удалитьсинхронизация (очевидно, не ломая вещи), или иметь отдельный dataHandler для потока (при условии, что рассматриваемый класс поддерживает это.)

...