Последствия создания нескольких экземпляров Runnable с общими объектами?

У меня есть программа, предназначенная для многопоточности. У меня есть класс ProcessRunnable, который обрабатывает данные с большим количеством операций ввода-вывода. Все классы ProcessRunnable запускаются в отдельных потоках, но создаются с общими экземплярами классов client/util.

Пример:

Client client = new Client();
Util util = new Util();

List<Runnable> runnables = new ArrayList<>();

for (int i; i < THREAD_COUNT; i++) {
    runnables.add(ProcessRunnable
                       .builder()
                       .client(client)
                       .util(util)
                       .build());
}

runnables.forEach(runnable -> new Thread(runnable).start());

Мне любопытно, является ли повторное использование одних и тех же экземпляров классов в runnables блокирующим поведением и, по сути, заставляет мою программу становиться однопоточной?


person pauld    schedule 23.03.2019    source источник
comment
^мило^ спасибо @AniketSahrawat   -  person pauld    schedule 23.03.2019
comment
@GhostCat достаточно честно - баллы набраны! Спасибо, что вступили в дискуссию, несмотря на лучшие практики;)   -  person pauld    schedule 23.03.2019
comment
Не беспокойтесь - еще раз спасибо!   -  person pauld    schedule 23.03.2019
comment
Пожалуйста. И, пожалуйста, не забывайте об удалении ненужных комментариев.   -  person GhostCat    schedule 23.03.2019


Ответы (1)


Здесь:

runnable -> new Thread(runnable).start()

Ключевым моментом, который на самом деле делает ваш код многопоточным, является то, что вы вызываете метод start() ваших объектов потока. Если бы вы просто вызвали метод run класса потока, то вы фактически получили бы, что «окружающий» поток выполняет всю работу.

Наконец, обратите внимание, что использование «голых» потоков напрямую не идеально. Об этом можно узнать, но Java предлагает важные абстракции, такие как ExecutorService, которые следует использовать вместо этого по разным причинам.

Основная причина избегать необработанных потоков: вам нужно вручную контролировать все тонкие детали. Сколько потоков следует использовать? Как насчет объединения и совместного использования потоков (создание потока сопряжено с большими накладными расходами, поэтому в реальном мире вы избегаете создания потоков для отдельных задач, а затем отбрасываете их, как это делает ваш код). Помимо этого: обычно вы хотите решить бизнес-проблему. Вы хотите использовать несколько потоков, чтобы предотвратить возникновение узких мест. Пример: вы хотите делать несколько запросов по сети параллельно для получения и обработки данных. Тогда вас действительно заботит только конечный результат, а не тонкости низкоуровневой работы с потоками! Затем вы, например, использовали бы объекты Future или CompleteableFuture.

Просто используйте поисковую систему и исследуйте эти термины, вы найдете много материала.

person GhostCat    schedule 23.03.2019
comment
Спасибо за ваш комментарий! Не могли бы вы рассказать о преимуществах использования ExecutorService и/или порекомендовать какой-либо материал для чтения, который, по вашему мнению, особенно информативен? Потоки обмениваются состоянием через BlockingQueues/BlockingDeques. Мне показалось относительно простым управлять потоками/завершать их надлежащим образом на основе BlocksQueues, но, конечно же, хотелось бы понять, как лучше это сделать! - person pauld; 23.03.2019
comment
Код, показанный выше, упрощен до объема исходного вопроса. Используемые потоки не выбрасываются, а работают долго и извлекают из PriorityBlockingQueue один элемент за раз для обработки. Я определенно вижу, как это можно улучшить, используя ExecutorService для управления потоками. В настоящее время все этапы обработки инкапсулированы в ProcessRunnable, включая несколько вызовов ввода-вывода. Как вы думаете, будут ли различия в производительности при разделении каждого шага и использовании CompletableFuture для всех операций ввода-вывода по сравнению с отправкой списка Runnables для службы-исполнителя для обработки/выполнения? - person pauld; 23.03.2019
comment
А еще - я умею пользоваться поисковиком! Чтобы не быть легкомысленным или ленивым, просто спросите, вы сами нашли какие-либо источники, которые, по вашему мнению, объясняют или показывают вещи особенно полезным способом. У вас, кажется, есть доверие к тому, где стоит задать вопрос! - person pauld; 23.03.2019