-
Notifications
You must be signed in to change notification settings - Fork 0
Home
CountDownLatch
Подходит лучше всего, самым первым приходит в голову при выборе подходящего инструмента. CountDownLatch предназначен для блокирования выполнения одного потока пока не будут выполнено определенное количество операций. В нашем случае у нас должно быть выполнено 5 операций, только после выполнения которых должна быть возобновлена работа Consumer’a.
Количество строк: 3
Количество дополнительных объектов: 1
Время выполнения: 2-5s
CyclicBarrier
Циклический барьер тоже неплох для реализации данной задачи. Задаем точкой сбора место, в котором Producer уже выполнил свою работу, ждем пока указанное количество потоков дойдут до указанного места и запускаем PostConsumer. Легко, просто, понятно.
Количество строк: 2
Количество дополнительных объектов: 1
Время выполнения: 2-5s
Semaphore
Семафор предназначен для ограничения одновременного доступа к некоторому ресурсу, что делает его не очень логичным и удобным инструментом для решения задачи ожидания завершения работы нескольких потоков. Непонимание коллег и проблемы с расширяемостью кода гарантированы.
Количество строк: 3-4
Количество дополнительных объектов: 1
Время выполнения: 9-11s + потерялся параллелизм
Exchanger
!!! ВНИМАНИЕ !!!
Сделано ради фана и опыта. Концептуально Exchange сюда подходит чуть менее, чем никак. Но - хэй, почему бы не попробовать?) К моему удивлению, время выполнения неплохое, но ваши коллеги скорее всего вызовут скорую.
Количество строк: 6
Количество дополнительных объектов: 2
Время выполнения: 3-5s
Phaser
Стильный модный молодёжный Phaser будет хорошо смотреться, когда заказчики "слегка" изменят условия задачи. Остальные плюсы у него такое же как у CountDownLatch. Он немного избыточен для решения конкретно этой задачи, но если делать задел на будущее - почему бы и нет? Дополнительный минус: только с версии api 21. Если таргетить аудиторию с 16, как указано в грэдле задания, данный метод не подходит.
Количество строк: 6
Количество дополнительных объектов: 1
Время выполнения: 2-5s
Do it raw, fully organic, GMO-free good old way
wait / notify
Классика (: Вводим объект-локер, сохраняем в нем счётчик, по которому будем проверять, все ли Producer-треды выполнились, после чего делаем notify и будим Consumer.
Так как это, считай, тот же самый CountDownLatch, лучше использовать его, т.к. количество строк увеличилось, а пользы от этого больше не стало.
Количество строк: 5-6
Количество дополнительных объектов: Зависит от реализации, можно совсем без дополнительных объектов, можно написать свой объект, который будет считать количество выполненных операций (привет, велосипедный CountDownLatch!)
Время выполнения: 11-16s + потерялся параллелизм
join
Join приостанавливает выполнение одного треда до окончания выполнения другого. Т.к. у нас не один, а целых 5 дополнительных тредов, придётся ввести дополнительный тред, который будет работать, пока они все не отработают.
В результате мы вводим дополнительный тред для создания тредов - это несколько портит картину и ухудшает понимание происходящего. Плюс, потокам придётся выполняться последовательно, либо нужно использовать механизм wait / notify. Избыточно, запутанно - не годится.
Количество строк: 10-15
Количество дополнительных объектов: 1-2
Время выполнения: 11-16s + потерялся параллелизм
Executor
Getting nice and wet with executors
Ого, а вот это огонь! Например, можно использовать fixed size pool, запустить столько потоков, сколько нужно, оповестить Executor о завершении и немного подождать. Главное, не запускать Executor из main-треда, а то он повесится до конца исполнения.
Количество строк: 5-7
Количество дополнительных объектов: 1
Время выполнения: 3-6s
Для синхронизации потоков в Java есть очень много методов, и можно реализовать ожидание окончания выполнения потоков даже теми инструментами, которые для этого, по идее, не предназначены. Для данного задания лучше всего подойдет CountDownLatch, т.к. он предназначен для решения именно этой задачи, плюс код практически не пришлось модифицировать, он остался понятным, логичным и читаемым, хотя можно рассмотреть варианты CyclicBarrier, Executor или Phaser (если вы не ограничены в версии api). По скорости эти методы почти не отличаются, разве что создание Executor несколько дольше.