Есть ли преимущество в использовании
java.util.concurrent.CountdownLatch
вместо того
java.util.concurrent.Semaphore ?
Насколько я могу судить, следующие фрагменты практически эквивалентны:
1. Семафор
final Semaphore sem = new Semaphore(0);
for (int i = 0; i < num_threads; ++ i)
{
Thread t = new Thread() {
public void run()
{
try
{
doStuff();
}
finally
{
sem.release();
}
}
};
t.start();
}
sem.acquire(num_threads);
2: CountDownLatch
final CountDownLatch latch = new CountDownLatch(num_threads);
for (int i = 0; i < num_threads; ++ i)
{
Thread t = new Thread() {
public void run()
{
try
{
doStuff();
}
finally
{
latch.countDown();
}
}
};
t.start();
}
latch.await();
За исключением того, что в случае № 2 защелка не может быть использована повторно и, что более важно, вам нужно заранее знать, сколько потоков будет создано (или дождитесь, пока все они будут запущены, прежде чем создавать защелку).
Так в какой ситуации защелка может быть предпочтительнее?
CountDownLatch используется для запуска серии потоков, а затем ожидания, пока все они не будут завершены (или пока они не вызовут
countDown()
заданное количество раз.Семафор используется для управления количеством параллельных потоков, использующих ресурс. Этот ресурс может быть чем-то вроде файла или может быть ЦП, ограничивая количество выполняемых потоков. Счетчик семафора может увеличиваться и уменьшаться по мере того, как разные потоки вызывают
acquire()
иrelease()
.В вашем примере вы по существу используете семафор как своего рода защелку Count UP . Учитывая, что ваше намерение состоит в том, чтобы дождаться завершения всех потоков, использование
CountdownLatch
делает ваше намерение более ясным.источник
Краткое содержание:
Semaphore
иCountDownLatch
служит другой цели.Используйте
Semaphore
для управления доступом потока к ресурсу.Используйте
CountDownLatch
для ожидания завершения всех потоковSemaphore
определение из Javadocs:Однако фактические объекты разрешений не используются;
Semaphore
просто продолжает подсчет числа имеющихся и действует соответствующим образом .Как это работает?
Семафоры используются для управления количеством параллельных потоков, использующих ресурс. Этот ресурс может быть чем-то вроде общих данных, блоком кода ( критический раздел ) или любым файлом.
Счетчик a
Semaphore
может увеличиваться и уменьшаться по мере того, как разные потоки вызываютacquire()
иrelease()
. Но в любой момент у вас не может быть большего количества потоков, чем количество семафоров.Semaphore
Сценарии использования:Взгляните на эту статью для использования семафоров.
CountDownLatch
определение из Javadocs:Как это работает?
CountDownLatch
работает за счет инициализации счетчика числом потоков, которое уменьшается каждый раз, когда поток завершает свое выполнение. Когда счетчик достигает нуля, это означает, что все потоки завершили свое выполнение, и поток, ожидающий защелки, возобновляет выполнение.CountDownLatch
Сценарии использования:Прочтите эту статью, чтобы
CountDownLatch
четко понять концепции.Взгляните также на Fork Join Pool в этой статье . Он имеет некоторое сходство с
CountDownLatch
.источник
Допустим, вы зашли в магазин профессиональных гольфистов в надежде найти четверку,
Когда вы стоите в очереди, чтобы получить время у одного из профессиональных продавцов магазина, по сути, вы звонили
proshopVendorSemaphore.acquire()
, как только у вас появляется времяproshopVendorSemaphore.release()
первого , вы звоните. Примечание: любой из бесплатных помощников может обслужить вас, то есть общий ресурс.Теперь вы подходите к стартеру, он начинается
CountDownLatch(4)
и звонкиawait()
ждать других, со своей стороны вы называетесь зарегистрированным т.е.CountDownLatch
.countDown()
и то же самое делает остальная четверка. Когда все приедут, стартер дает (await()
звонок возвращается)Теперь, после девяти лунок, когда каждый из вас делает перерыв, гипотетически позволяет снова задействовать стартера, он использует «новичок»
CountDownLatch(4)
для выхода из лунки 10, такое же ожидание / синхронизация, как и в лунке 1.Однако, если бы стартер использовал a
CyclicBarrier
для начала, он мог бы сбросить тот же экземпляр в лунке 10 вместо второй защелки, которая использует & throw.источник
Если посмотреть на свободно доступный источник, в реализации этих двух классов нет ничего волшебного, поэтому их производительность должна быть примерно одинаковой. Выберите тот, который делает ваши намерения более очевидными.
источник
CountdownLatch
заставляет потоки ждатьawait()
метода, пока счетчик не достигнет нуля. Так что, возможно, вы хотите, чтобы все ваши потоки ждали до трех вызовов чего-либо, тогда все потоки могут уйти. АLatch
вообще сбросить нельзя.A
Semaphore
позволяет потокам получать разрешения, что предотвращает одновременное выполнение слишком большого количества потоков, блокируя, если он не может получить разрешения, необходимые для продолжения. Разрешения можно вернуть,Semaphore
разрешив другим ожидающим потокам продолжить работу.источник