Как выполнять операторы в цикле for-each параллельно в Java? - PullRequest
0 голосов
/ 09 апреля 2019

У меня есть фрагмент кода, который выглядит следующим образом:

public List<Restaurant> getAllRestaurants() {
    List<Restaurant> restaurants = getRestaurants().subList(0, 7); // This takes 234 ms to execute on average.    

    // There are 7 items in the restaurants list
    for (Restaurant restaurant : restaurants) {
        PlacesAPIResponse response = callGooglePlacesAPI(restaurant); // A call to the Google API should take 520ms for a given restaurant
        restaurant.setRating(response.getRating());
    }
    return restaurants;
}

Если я сделаю вышеупомянутые операторы в цикле for-each, как показано, я ожидаю, что общее время метода будет 234ms + (7*520)ms = 3874ms, поскольку операторы выполняются последовательно.Это слишком медленно, поэтому я бы хотел распараллелить операторы в цикле for-each, чтобы я вызывал API Google Places одновременно для каждого ресторана в списке.Теоретически время ответа должно быть 234ms + max(API call for Restaurant 1, ..., API call for Restaurant 7) = 234ms + 520ms = 754ms, поскольку вызовы API Google выполняются параллельно.

Согласно эта ссылка (Java 8: параллельный цикл FOR) ,Я должен иметь возможность использовать parallelStream() для одновременного выполнения утверждений, подобных этому:

long startTime = System.currentTimeMillis();
restaurants.parallelStream().forEach(restaurant -> {
    PlacesAPIResponse response = callGooglePlacesAPI(restaurant);
    restaurant.setRating(response.getRating());
});
long endTime = System.currentTimeMillis();
System.out.println("Calling Google Places API took " + (endTime - startTime) + " milliseconds");

Кажется, что это вызывает API Google Places для каждого ресторана параллельно, но теперь каждый вызов API Google Placesкажется, занимает все больше времени.Вот вывод моих временных меток:

getRestaurants() took 234 milliseconds
Took 335 milliseconds to call Google Places API for Restaurant 1
Took 337 milliseconds to call Google Places API for Restaurant 2
Took 671 milliseconds to call Google Places API for Restaurant 3
Took 742 milliseconds to call Google Places API for Restaurant 4
Took 1086 milliseconds to call Google Places API for Restaurant 5
Took 1116 milliseconds to call Google Places API for Restaurant 6
Took 1470 milliseconds to call Google Places API for Restaurant 7
Calling Google Places API took 1473 milliseconds

1734ms намного больше, чем ожидалось 754ms, который я ожидал.Я пробовал параллельные потоки, а также ExecutorService для одновременного вызова API Google Адресов, но не могу получить желаемое время ответа.Может кто-то указать мне верное направление?Спасибо.

РЕДАКТИРОВАТЬ: Вот то, что я пробовал с ExecutorService, в соответствии с этим постом (Есть ли простой способ распараллелить цикл foreach в Java?) :

startTime = System.currentTimeMillis();
ExecutorService exe = Executors.newFixedThreadPool(2);   // 2 can be changed of course
for (Restaurant restaurant : restaurants) {
    exe.submit(() -> {
        PlacesAPIResponse response = callGooglePlacesAPI(restaurant); // A call to the Google API should take 520ms for a given restaurant
        restaurant.setRating(response.getRating());
    });
}    

exe.shutdown();
try {
    exe.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
    e.printStackTrace();
}    

endTime = System.currentTimeMillis();
System.out.println("Calling Google Places API took " + (endTime - startTime) + " milliseconds");
return restaurants; 

Вот вывод моих временных меток:

getRestaurants() took 234 milliseconds
Took 464 milliseconds to call Google Places API for Restaurant 1
Took 575 milliseconds to call Google Places API for Restaurant 2
Took 452 milliseconds to call Google Places API for Restaurant 3
Took 420 milliseconds to call Google Places API for Restaurant 4
Took 414 milliseconds to call Google Places API for Restaurant 5
Took 444 milliseconds to call Google Places API for Restaurant 6
Took 422 milliseconds to call Google Places API for Restaurant 7
Calling Google Places API took 1757 milliseconds

Время отклика этого метода по-прежнему 234ms + 1757 ms вместо 234ms + 575ms, и я не понимаю, почему,

Ответы [ 2 ]

1 голос
/ 09 апреля 2019

Лучше всего здесь использовать executorService и предоставлять задачи для них как отдельные Runnable ().

Или вы можете использовать Future здесь.

0 голосов
/ 09 апреля 2019

Полагаю, узким местом является подключение к Интернету или серверу Google Places, а не ваш цикл. Сервер распознает тот же IP-адрес и поэтому ставит в очередь ваши запросы, чтобы защитить себя от атак типа «отказ в обслуживании». Это означает, что ваш цикл работает параллельно, но интернет-запросы стекаются на сервере, поэтому каждый запрос все чаще занимает больше времени, пока он не будет получен ответ и возвращен. Чтобы обойти это, вам нужно что-то вроде бот-сети (отправляющей каждый запрос с разных компьютеров) или, возможно, Google Places продаст вам специальное соединение для параллельных запросов.

...