что такое барьер в канкаренси
Справочник по синхронизаторам java.util.concurrent.*
Целью данной публикации не является полный анализ синхронизаторов из пакета java.util.concurrent. Пишу её, прежде всего, как справочник, который облегчит вхождение в тему и покажет возможности практического применения классов для синхронизации потоков (далее поток = thread).
В java.util.concurrent много различных классов, которые по функционалу можно поделить на группы: Concurrent Collections, Executors, Atomics и т.д. Одной из этих групп будет Synchronizers (синхронизаторы).
Синхронизаторы – вспомогательные утилиты для синхронизации потоков, которые дают возможность разработчику регулировать и/или ограничивать работу потоков и предоставляют более высокий уровень абстракции, чем основные примитивы языка (мониторы).
Semaphore
Синхронизатор Semaphore реализует шаблон синхронизации Семафор. Чаще всего, семафоры необходимы, когда нужно ограничить доступ к некоторому общему ресурсу. В конструктор этого класса ( Semaphore(int permits) или Semaphore(int permits, boolean fair) ) обязательно передается количество потоков, которому семафор будет разрешать одновременно использовать заданный ресурс.
Рассмотрим следующий пример. Существует парковка, которая одновременно может вмещать не более 5 автомобилей. Если парковка заполнена полностью, то вновь прибывший автомобиль должен подождать пока не освободится хотя бы одно место. После этого он сможет припарковаться.
CountDownLatch
CountDownLatch (замок с обратным отсчетом) предоставляет возможность любому количеству потоков в блоке кода ожидать до тех пор, пока не завершится определенное количество операций, выполняющихся в других потоках, перед тем как они будут «отпущены», чтобы продолжить свою деятельность. В конструктор CountDownLatch ( CountDownLatch(int count) ) обязательно передается количество операций, которое должно быть выполнено, чтобы замок «отпустил» заблокированные потоки.
Блокировка потоков снимается с помощью счётчика: любой действующий поток, при выполнении определенной операции уменьшает значение счётчика. Когда счётчик достигает 0, все ожидающие потоки разблокируются и продолжают выполняться (примером CountDownLatch из жизни может служить сбор экскурсионной группы: пока не наберется определенное количество человек, экскурсия не начнется).
CyclicBarrier
CyclicBarrier реализует шаблон синхронизации Барьер. Циклический барьер является точкой синхронизации, в которой указанное количество параллельных потоков встречается и блокируется. Как только все потоки прибыли, выполняется опционное действие (или не выполняется, если барьер был инициализирован без него), и, после того, как оно выполнено, барьер ломается и ожидающие потоки «освобождаются». В конструктор барьера ( CyclicBarrier(int parties) и CyclicBarrier(int parties, Runnable barrierAction) ) обязательно передается количество сторон, которые должны «встретиться», и, опционально, действие, которое должно произойти, когда стороны встретились, но перед тем когда они будут «отпущены».
Рассмотрим следующий пример. Существует паромная переправа. Паром может переправлять одновременно по три автомобиля. Чтобы не гонять паром лишний раз, нужно отправлять его, когда у переправы соберется минимум три автомобиля.
Exchanger
Exchanger (обменник) может понадобиться, для того, чтобы обменяться данными между двумя потоками в определенной точки работы обоих потоков. Обменник — обобщенный класс, он параметризируется типом объекта для передачи.
Рассмотрим следующий пример. Есть два грузовика: один едет из пункта A в пункт D, другой из пункта B в пункт С. Дороги AD и BC пересекаются в пункте E. Из пунктов A и B нужно доставить посылки в пункты C и D. Для этого грузовики в пункте E должны встретиться и обменяться соответствующими посылками.
Phaser
Phaser (фазер), как и CyclicBarrier, является реализацией шаблона синхронизации Барьер, но, в отличии от CyclicBarrier, предоставляет больше гибкости. Этот класс позволяет синхронизировать потоки, представляющие отдельную фазу или стадию выполнения общего действия. Как и CyclicBarrier, Phaser является точкой синхронизации, в которой встречаются потоки-участники. Когда все стороны прибыли, Phaser переходит к следующей фазе и снова ожидает ее завершения.
Параметр parties указывает на количество сторон-участников, которые будут выполнять фазы действия. Первый конструктор создает объект Phaser без каких-либо сторон, при этом барьер в этом случае тоже «закрыт». Второй конструктор регистрирует передаваемое в конструктор количество сторон. Барьер открывается когда все стороны прибыли, или, если снимается последний участник. (У класса Phaser еще есть конструкторы, в которые передается родительский объект Phaser, но мы их рассматривать не будем.)
Рассмотрим следующий пример. Есть пять остановок. На первых четырех из них могут стоять пассажиры и ждать автобуса. Автобус выезжает из парка и останавливается на каждой остановке на некоторое время. После конечной остановки автобус едет в парк. Нам нужно забрать пассажиров и высадить их на нужных остановках.
Если кому-нибудь пригодилось, то я очень рад=)
Более подробно о Phaser здесь.
Почитать ещё о синхронизаторах и посмотреть примеры можно здесь.
Синхронизаторы пакета concurrent
Объекты синхронизации Synchroniser из пакета java.util.concurrent включают :
Semaphore | объект синхронизации, ограничивающий количество потоков, которые могут «войти» в заданный участок кода; |
CountDownLatch | объект синхронизации, разрешающий вход в заданный участок кода при выполнении определенных условий; |
CyclicBarrier | объект синхронизации типа «барьер», блокирующий выполнение определенного кода для заданного количества потоков; |
Exchanger | объект синхронизации, позволяющий провести обмен данными между двумя потоками; |
Phaser | объект синхронизации типа «барьер», но в отличие от CyclicBarrier, предоставляет больше гибкости. |
Объект синхронизации Semaphore
Как было отмечено выше, Semaphore ограничивает доступ к определенному участку кода, иначе говоря, к общему ресурсу, в качестве которого могут выступать программые/аппаратные ресурсы или файловая система.
Для управления доступом к общему ресурсу Semaphore использует счетчик. Если значение счетчика больше нуля, то поток исполнения получает разрешение, после чего значение счетчика семафора уменьшается на единицу. При значении счетчика равным нулю очередному потоку исполнения в доступе будет отказано, и он будет заблокирован до тех пор, пока не будут освобождены ресурсы.
Как только один из потоков исполнения освободит ресурсы, т.е. завершит исполнение определенного участка кода, то значение счетчика семафора увеличивается на единицу. Если в это время имеется ожидающий разрешения другой поток исполнения, то он сразу же его получает.
Конструкторы Semaphore
Класс Semaphore имеет два приведенных ниже конструктора :
Параметр permits определяет исходное значение счетчика разрешений, т.е. количество потоков исполнения, которым может быть одновременно предоставлен доступ к общему ресурсу. По умолчанию ожидающим потокам предоставляется разрешение в неопределенном порядке. Если же использовать второй конструктор и параметру справедливости fair присвоить значение true, то разрешения будут предоставляться ожидающим потокам исполнения в том порядке, в каком они его запрашивали.
Метод получения разрешения acquire
Чтобы получить у семафора разрешение необходимо вызвать у него один из перегруженных методов acquire :
Первый метод без параметра запрашивает одно разрешение, а второй в качестве параметра использует количество разрешений. Обычно используется первый метод. Если разрешение не будет получено, то исполнение вызывающего потока будет приостановлено до тех пор, пока не будет получено разрешение, т.е. поток блокируется.
Освобождение ресурса
Чтобы освободить разрешение у семафора следует вызвать у него один из перегруженных методов release :
В первом методе освобождается одно разрешение, а во втором — количество разрешений, обозначенное параметром number.
Пример использования Semaphore
В примере несколько всадников с лошадьми должны пройти контроль перед скачками. Количество контроллеров меньше количества всадников, поэтому некоторые всадники будут дожидаться, пока не освободиться один из контроллеров.
Общий ресурс CONTROL_PLACES, символизирующий контролеров и используемый всеми потоками, выделен оператором synchronized. С помощью семафора осуществляется контроль доступа только одному потоку.
Результат выполнения примера с семафором
Обратите внимание, что «всадник 3» завершил проверку, после чего «всадник 6» вошел в блокируемый участок кода. А вот «всадник 7» успел раньше вывести сообщение о входе в блокируемый участок кода, чем «всадник 1» сообщил о его «покидании».
Объект синхронизации CountDownLatch
Объект синхронизации потоков CountDownLatch представляет собой «защелку с обратным отсчетом» : несколько потоков, выполняя определенный код, блокируются до тех пор, пока не будут выполнены заданные условия. Количество условий определяются счетчиком. Как только счетчик обнулится, т.е. будут выполнены все условия, самоблокировки выполняемых потоков снимаются, и они продолжают выполнение кода.
Таким образом, CountDownLatch также, как и Semaphore, работает со счетчиком, обнуление которого снимает самоблокировки выполняемых потоков. Конструктор CountDownLatch :
Параметр number определяет количество событий, которые должны произойти до того момента, когда будет снята самоблокировка.
Метод самоблокировки await
CountDownLatch имеет два перегруженных метода await для самоблокировки :
В первом методе ожидание длится до тех пор, пока счетчик CountDownLatch не достигнет нуля. А во втором методе ожидание длится только в течение определенного периода времени, определяемого параметром ожидание wait. Время ожидания указывается в единицах unit объекта перечисления TimeUnit, определяюший временно́е разбиение. Существуют следующие значения данного перечисления :
Метод уменьшения счетчика countDown
Чтобы уменьшить счетчик объекта CountDownLatch следует вызвать метод countDown :
Примером CountDownLatch может служить паром, собирающий определенное количество транспорта и пассажиров, или экскурсовод, собирающий группу из заданного количества туристов.
Пример использования CountDownLatch
В примере несколько всадников должны подъехать к барьеру. Как только все всадники выстроятся перед барьером, будут даны команды «На старт», «Внимание», «Марш». После этого барьер опустится и начнутся скачки. Объект синхронизации CountDownLatch выполняет роль счетчика количества всадников и команд.
При «выходе на старт» поток вызывает методы countDown, уменьшая значение счетчика на 1, и await, блокируя самого себя в ожидании обнуления счетчика «защелки». Как только все потоки выстроятся на «старте» с интервалом в 1 сек. подаются команды. Каждая команда сопровождается уменьшением счетчика. После обнуления счетчика «защелки» потоки продолжают выполнение дальнейшего кода.
Результат выполнения примера
Объект синхронизации CyclicBarrier
Объект синхронизации CyclicBarrier представляет собой барьерную синхронизацию, используемую, как правило, в распределённых вычислениях. Особенно эффективно использование барьеров при циклических расчетах. При барьерной синхронизации алгоритм расчета делят на несколько потоков. С помощью барьера организуют точку сбора частичных результатов вычислений, в которой подводится итог этапа вычислений.
В исходном коде барьер для группы потоков означает, что каждый поток должен остановиться в определенном месте и ожидать прихода остальных потоков группы. Как только все потоки достигнут барьера, их выполнение продолжится.
Класс CyclicBarrier имеет 2 конструктора :
В первом конструкторе задается количество потоков, которые должны достигнуть барьера, чтобы после этого одновременно продолжить выполнение кода. Во втором конструкторе дополнительно задается реализующий интерфейс Runnable класс, который должен быть запущен после прихода к барьеру всех потоков. Поток запускать самостоятельно НЕ НУЖНО. CyclicBarrier это делает автоматически.
Для указания потоку о достижении барьера нужно вызвать один из перегруженных методов await :
Назначение параметров wait и unit у второго метода описано выше (см. CountDownLatch).
Циклический барьер CyclicBarrier похож на CountDownLatch. Главное различие между ними связано с тем, что «защелку» нельзя использовать повторно после того, как её счётчик обнулится, а барьер можно использовать (в цикле). С точки зрения API циклический барьер CyclicBarrier имеет только метод самоблокировки await и не имеет метода декрементации счетчика, а также позволяет подключить и автоматически запускать дополнительный потоковый класс при достижении барьера всех исполняемых потоков.
Пример использования CyclicBarrier
В примере организуется переправа. Паром может вместить только 3 автомобиля. Количество автомобилей 9. Роль парома выполняет объект синхронизации FerryBarrier, которому в качестве второго параметра передается реализующий интерфейс Runnable класс FerryBoat. Как только 3 потока достигнут барьера автоматически будет запущен FerryBoat, после завершения работы которого потоки продолжают свою работу.
Обратите внимание, что потоки подходят к барьеру с интервалом в 400 ms. Время задержки у барьера/переправы (после того, как собралось необходимое количество потоков), составляет 500 ms, если не считать время вывода сообщений в консоль. За это время к барьеру успевает подойти еще один поток. Что мы и видим при выводе сообщений в консоль.
Результат выполнения примера
Варьируя временем на переправе и временем прихода автомобилей на переправу, можно либо заставить паром простаивать, либо будут простаивать автомобили на переправе.
Объект синхронизации Exchanger
Класс Exchanger (обменник) предназначен для упрощения процесса обмена данными между двумя потоками исполнения. Принцип действия класса Exchanger связан с ожиданием того, что два отдельных потока должны вызвать его метод exchange. Как только это произойдет, Exchanger произведет обмен данными, предоставляемыми обоими потоками.
Обменник является обобщенным классом, он параметризируется типом объекта передачи :
Необходимо отметить, что обменник поддерживает передачу NULL значения, что дает возможность использовать его для передачи объекта в одну сторону или места синхронизации двух потоков.
Exchanger содержит перегруженный метод exchange, имеющий следующие формы :
Параметр buffer является ссылкой на обмениваемые данные. Метод возвращает данные из другого потока исполнения. Вторая форма метода позволяет определить время ожидания. Параметры wait и unit описаны выше. Метод exchange, вызванный в одном потоке, не завершится успешно до тех пор, пока он не будет вызван из второго потока исполнения.
Пример использования Exchanger
В примере использования объекта синхронизации Exchanger два почтальона из пунктов А и Б отправляются в соседние поселки В и Г доставить письма. Каждый из почтальонов должен доставить по письму в каждый из поселков. Чтобы не делать лишний круг, они встречаются в промежуточном поселке Д и обмениваются одним письмом. В результате этого каждому из почтальонов придется доставить письма только в один поселок. В примере все «шаги» почтальонов фиксируются выводом соответствующих сообщений в консоль.
Результат выполнения примера
В консоль будет выведена следующая информация :
Объект синхронизации Phaser
Phaser (фазировщик), как и CyclicBarrier, является реализацией объекта синхронизации типа «Барьер» (CyclicBarrier). В отличии от CyclicBarrier, Phaser предоставляет больше гибкости. Чтобы лучше понять Phaser, можно привести два наглядно демонстрирующих его использование примера.
В качестве первого примера можно рассмотреть несколько потоков исполнения, реализующих процесс обработки заказов из трех стадий. На первой стадии отдельные потоки исполнения проверяют сведения о клиенте, наличие товара на складе и их стоимость. На второй стадии вычисляется стоимость заказа и стоимость доставки. На заключительной стадии подтверждается оплата и определяется ориентировочное время доставки. Во втором примере несколько потоков реализуют перевозку пассажиров городским транспортом. Пассажиры ожидают транспорт на разных остановках. Транспорт, останавливаясь на остановках, одних пассажиров «сажает», других «высаживает».
В этих примерах общим является то, что один объект синхронизации Phaser, исполняющий роль заказа и транспорта, играет главную роль, а другие потоки вступают в работу при определенном состоянии Phaser. Таким образом, класс Phaser позволяет определить объект синхронизации, ожидающий завершения определенной фазы. После этого он переходит к следующей фазе и снова ожидает ее завершения.
Важные особенности Phaser :
Для создания объекта Phaser используется один из конструкторов :
Параметр parties определяет количество участников, которые должны пройти все фазы. Первый конструктор создает объект Phaser без каких-либо участников. Второй конструктор регистрирует передаваемое в конструктор количество участников. Третий и четвертый конструкторы дополнительно устанавливают родительский объект Phaser.
При создании экземпляр класса Phaser находится в нулевой фазе. В очередном состоянии (фазе) синхронизатор находится в ожидании до тех пор, пока все зарегистрированные потоки не завершат данную фазу. Потоки извещают об этом, вызывая один из методов arrive() или arriveAndAwaitAdvance().
Методы объекта синхронизации Phaser
Метод | Описание |
---|---|
int register() | Метод регистририрует участника и возвращает номер текущей фазы. |
int arrive() | Метод указывает на завершения выполнения текущей фазы и возвращает номер фазы. Если же работа Phaser закончена, то метод вернет отрицательное число. При вызове метода arrive поток не приостанавливается, а продолжает выполняться. |
int arriveAndAwaitAdvance() | Метод вызывается потоком/участником, чтобы указать, что он завершил текущую фазу. Это аналог метода CyclicBarrier.await(), сообщающего о прибытии к барьеру. |
int arriveAndDeregister() | Метод arriveAndDeregister сообщает о завершении всех фаз участником и снимается с регистрации. Данный метод возвращает номер текущей фазы или отрицательное число, если Phaser завершил свою работу |
int getPhase() | Получение номера текущей фазы. |
Следующее анимационное изображение наглядно демонстрирует работу объекта синхронизации Phaser — участник регистрируется в определенной фазе синхронизатора; при переходе синхронизатора в заданное состояние (фазу) участник снимается с регистрации. Количество участников в разных фазах синхронизатора может отличаться.
Пример использования Phaser
В примере PhaserExample создается несколько потоков, играющих роль пассажиров. Phaser играет роль метро, которое должно проследовать вдоль нескольких станций. Каждая станция (фаза) имеет свой номер. Класс Passenger играет роль пассажира, который на одной из станции должен зайти в вагон, а на другой выйти. Количество пассажиров, а также их места посадки и высадки, формируются случайным образом.
Листинг класса Passenger
Конструктор класса Passenger получает значение идентификатора, номера станций посадки и назначения (высадки). При создании объекта в консоль выводится информация о пассажире (метод toString).
Как только Phaser переходит в определенную фазу, номер которой соответствует станции посадки пассажира, то поток данного Passenger стартует (run) и выводит в консоль сообщение, что пассажир вошел в вагон, т.е. находится в ожидании следующей станции/фазы (arriveAndAwaitAdvance). Если следующая станция/фаза не будет соответствовать станции назначения, то Passenger продолжит свой путь. Как только Phaser перейдет в фазу, номер которой соответствует номеру станции назначания пассажира, то цикл контроля завершится и поток продолжит работу. С задержкой в 500 ms он сообщит, что покинул вагон и отменит регистрацию в Phaser (arriveAndDeregister).
Таким образом, поток/пассажир дожидается свой фазы/станции в цикле, выделенной в коде пунктирными комментариями. Вызов метода arriveAndAwaitAdvance возвращает значение следующего номера фазы, т.е. участник будет вызван при переходе Phaser в новое состояние. Если в этом состоянии значение фазы (getPhase) будет соответствовать номеру destination, то цикл прервется, в противном случае, ожидание следующей фазы и повторное выполнение проверки условия while.
Примечание : Passenger является внутренним классом примера/класса PhaserExample, и для описания вынесен из общего кода, чтобы не загромождать листинг.
Листинг примера PhaserExample
В примере сначала создается объект синхронизации PHASER. После этого формируется массив пассажиров. При создании объекта Passenger случайным образом определяются станция посадки и станция назначения. После того, как массив пассажиров подготовлен, PHASER в цикле начинает менять свое состояние. На каждом шаге выполняется проверка «станции посадки пассажира». Если она соответствует значению фазы, то данный пассажир входит в вагон метро, т.е. регистрируется в PHASER и поток стартует. Таким образом, регистрация участников (исполнительных потоков) выполняется при нахождении PHASER в определенном состоянии/фазе. Пассажир покинет вагон при достижении метро станции назначения, т.е. при нахождении PHASER в соответствующей фазе. Но это произойдет уже в коде класса Passenger, рассмотренного выше.
Русские Блоги
Одновременные инструменты синхронизации Барьер CyclicBarrier
Вступление
Буквальность из CyclicBarrier является барьером, который может быть переработан (барьер). То, что он хочет сделать блокируется, когда множество потоков прибыли в качестве барьера (также можно назвать синхронной точки) до последней нитки не достигнет барьера, барьер будет только открыть дверь, все нити, которые блокируются Заграждение продолжать работу. Конструктор по умолчанию CyclicBarrier является CyclicBarrier (INT Стороны), ее параметр указывает количество потоков, а каждый поток вызывает метод говорит AWAIT CyclicBarrier я достиг барьера, и текущий поток блокируется.
Код экземпляра заключается в следующем:
CyclicBarrier также обеспечивает более продвинутый конструктор CyclicBarrier (INT) Стороны, которая используется для выполнения Barrieraction, который имеет приоритет, когда поток поступает на барьере, который удобен для обработки более сложной бизнес-сцены. Кодовое шоу, как показано ниже:
Сценарий приложения Cyclicbarrier
CyclicBarrier может быть использован в многопоточных вычислительных данных, применение сценарий окончательных результатов расчета слияния. Например, мы сохранили все банки пользователя с Excel. Каждый лист сохраняет счет почти один год. Сейчас требуется подсчитать средний банк пользователя, первое использование многопоточности течь через банки в каждом листе. После того, как все они будут реализованы, получить средний банк каждого листа, наконец, использовать Barrieraction для вычисления результатов расчета этих потоков, рассчитать среднесуточный поток банка в Excel.
Различия между CyclicBarrier и CountDownLatch
Использование код IsBroken выглядит следующим образом:
Что такое барьер в канкаренси
— Сегодня будет небольшая, но интересная и полезная тема – сортировки коллекций.
— Сортировка? Я что-то про это слышал.
— Давным-давно каждый программист обязан был уметь писать сортировку. Умел и писал. Но те времена канули в лету. Сегодня написание своей сортировки считается дурным тоном, как и написание всего, что уже было придумано.
В Java (да и других языках программирования) сортировки уже реализованы. Твоя задача – научиться правильно пользоваться тем, что есть.
— У вспомогательного класса Collections есть статический метод sort, который используется для сортировки коллекций, а если точнее – списков. Элементы в коллекциях Map и Set не имеют порядка/номера, значит, и сортировать там нечего.
— Да, я вспомнил, я когда-то уже использовал этот метод для сортировки списка чисел.
— Отлично. Но этот метод гораздо мощнее чем, кажется на первый взгляд. Он может сортировать не только числа, но и любые объекты, по любым критериям. И помогают ему в этом два интерфейса: Comparable и Comparator.
Иногда бывает нужно отсортировать объекты, а не числа. Например, у тебя есть список людей, и ты хочешь отсортировать их по возрасту. Для этого есть интерфейс Comparable.
Давай я сначала покажу тебе пример, и все станет понятнее:
Пример |
---|
public class Woman implements Comparable < public int age; public Woman(int age) < Collections.sort(women); |
Чтобы объекты можно было сортировать, сначала нужно научиться их сравнивать. Для этого и используется Comparable. Интерфейс Comparable является generic’ом – т.е. типом с параметром. У него всего один generic-метод – compare(To). В этом методе и происходит сравнение переданного объекта и текущего (this). Т.е. надо переопределить этот метод в своем классе и сравнить в нем текущий объект (this) с переданным.
— А как работает compare? Я думал, что он будет возвращать true/false в зависимости от того – больше переданный объект или меньше.
— Тут все немного хитрее. Метод compare возвращает не true/false, а значение типа int. На самом деле так сделано для простоты.
Когда компьютеру нужно определить больше ли одно число, чем другое, он просто вычитает из первого числа второе, а потом смотрит, что получилось. Если 0 – числа равны, если получилось число меньше нуля, то второе число больше, а если результат больше нуля, то больше уже первое число.
Тут используется та же логика. Согласно спецификации метод compare должен вернуть ноль, если сравниваемые объекты равны. Если метод compare вернул число больше нуля, это значит, что наш (this) объект больше, чем переданный. Если метод compare вернул число меньше нуля, то объект this меньше чем переданный.
— Да, но если ты сравниваешь объекты просто по какому-то параметру-числу, то можешь просто вернуть разницу между ними – вычесть один из другого. Как это и сделано в примере выше.
— Вроде все понятно. Хотя может и не все. Но почти все.
— Отлично. Теперь рассмотрим более практическую задачу. Ты написал крутой сайт по пошиву женской одежды в Китае. Для описания своих пользователей ты используешь класс Woman. Ты даже сделал страницу с таблицей, где можешь посмотреть их всех. Но есть проблема…
Объект Woman содержит у тебя не только возраст, а еще целую кучу данных: имя, фамилию, рост, вес, количество детей, …
В таблице пользователей есть много колонок, и тут встает вопрос: а как сортировать пользователей по разным критериям? По весу, по возрасту, по фамилии?
— Гм. Действительно, часто вижу таблицы с сортировкой колонок. И как это сделать?
— А для этого есть второй интерфейс, о котором я хотел тебе сегодня рассказать – это интерфейс Comparator. И у него тоже есть метод compare, только он принимает не один параметр, а два. Вот как это работает:
Пример |
---|
public class Woman < public int age; public int childrenCount; public int weight; public int height; public String name; public Woman(int age) < |
Пример использования: |
public stati void main(String[] args ) < ArrayList women = new ArrayList (); women.add(new Woman(18)); women.add(new Woman(21)); women.add(new Woman(5)); Collections.sort(women, compareByHeight ); |
При использовании интерфейса Comparator, логика сравнения пары объектов не прячется внутрь класса/объекта, а реализуется в отдельном классе.
— Т.е. я могу сделать несколько классов, реализующих интерфейс Comparator, но в каждом из них сравнивать разные параметры? В одном – weight, в другом – age, в третьем – height?
— Да, это очень просто и удобно.
Мы просто вызываем метод Collections.sort, передаем туда список объектов и еще специальный объект во втором параметре, который реализует интерфейс Comparator и говорит, как правильно сравнивать пары объектов в процессе сортировки.
— Гм. Вроде все понятно. Дай-ка я сам попробую. Допустим, мне нужно отсортировать пользователей по весу, это будет так:
Collections.sort(women, compareByWeight );
— Отлично. А если я хочу отсортировать в обратном порядке?
— А подумать? Ответ очень простой!
— А если я хочу сортировать по фамилии? Как сортировать строки, Билаабо?
— А у строк уже реализован метод compare, надо просто вызвать его:
Пример кода, пользователи сортируются по имени: |
---|
Comparator compareByName = new Comparator () < public int compare (Woman o1, Woman o2) < return o1.name. compareTo (o2.name); > >; Collections.sort(women, compareByName ); |
— Это был отличный урок, Билаабо, спасибо тебе большое.
— И тебе спасибо, друг!
2. Задачи на сортировку и comparator
Задачи |
---|
1. Почитать в инете про медиану выборки3. Разделяемые ресурсы, Конфликты, Проблема совместного доступа— Привет, Амиго! Хочу тебе рассказать о совместном использовании ресурсов. Разными нитями, ясное дело. Я все время говорю о проблемах при работе нескольких нитей и о том, как их решать. Это не значит, что использование нитей – это плохо. Нити – это очень мощный инструмент. Фактически, они позволяют увеличить скорость, и даже надежность работы твоей программы. Чем сложнее программа – тем больше в ней нитей и различных самостоятельных частей. Разбиение программы на независимые (слабосвязанные) части очень выгодно. Представь, что твоя программа внутри разбита на 100 нитей. Но у тебя всего двухъядерный процессор. Это значит, что на каждом ядре исполняется в среднем 50 нитей. Если же тебе нужно нарастить мощность программы, ты просто покупаешь двухпроцессорный сервер и пару крутых процессоров для него. В результате у него суммарно может быть до 32- ядер, и производительность твоей программы может вырасти в 2-20 раз. В зависимости от того, насколько действительно независимых частей она разбита. Это одна из причин доминирования Java в секторе Enterprise-разработки. Если у компании есть сложная программа для внутренних нужд, которую пишут 20 разработчиков, то купить еще один сервер гораздо дешевле, чем ускорить программу в 2 раза. — Так вот оказывается в чем дело. — Но! Каждый раз, когда разработчик решает использовать еще одну нить, он решает одну проблему, а создает две. Слишком много нитей не увеличат производительность программы до бесконечности. Во-первых, в любой программе есть работа, которую невозможно разбить на части и выполнять параллельно в разных нитях. Во-вторых, все нити выполняются на одном и том же процессоре. Чем больше нитей, тем медленнее работает каждая из них. И, самое главное – нити часто используют одни и те же объекты (их обычно называют разделяемыми ресурсами). Например, нить хочет сохранить в файл информацию о сделанной работе. Если таких нитей несколько, и они хотят записать информацию в один файл – они будут мешать друг другу. Чтобы в файле не было мешанины, каждая нить пользуется уникальным доступом к файлу – т.е. пока файлом пользуется одна нить, другие ждут. — Да, я помню, это делается с помощью ключевого слова synchronized. — А если нити пишут в разные файлы? — Формально – это разные объекты, но жесткий диск же один. — Т.е. реально что-то распараллелить можно только внутри процессора? — Формально – Да, как только твоей нити надо что-то еще кроме данных, которые у нее есть, это что-то уже может быть занято другой нитью и придется ждать. — И что же делать? Как узнать – делать много нитей или нет? — Это определяется непосредственно архитектурой программы. У любого проекта есть его «архитектор», который знает все «ресурсы», которые используются в программе, знает их ограничения, и насколько они хорошо/плохо параллелятся. — Тут есть два варианта: а) поработать под началом такого специалиста б) набить шишек самому — Я – робот, у меня не бывает шишек – только вмятины. — Ну, значит, набить вмятин. — Ясно, спасибо. Ты прояснила некоторые вопросы, о которых я уже начал ломать голову.
|