Skip to content
aleien edited this page Jul 17, 2016 · 3 revisions

Synchronizers

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

System synchronization

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 + потерялся параллелизм

Software synchronization

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 несколько дольше.

Clone this wiki locally