CountDownLatch против Семафора

Есть ли преимущества использования

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 защелка не может быть повторно использована, и, что более важно, вам нужно заранее знать, сколько потоков будет создано (или дождитесь, пока они все будут запущены до создания защелки.)

Итак, в какой ситуации может быть предпочтительна защелка?

Ответ 1

Захват CountDown часто используется для полной противоположности вашего примера. Как правило, у вас будет много потоков, блокирующих "ожидание()", которые будут запускаться одновременно, когда счетчик достигнет нуля.

final CountDownLatch countdown = new CountDownLatch(1);
for (int i = 0; i < 10; ++ i){
   Thread racecar = new Thread() {    
      public void run()    {
         countdown.await(); //all threads waiting
         System.out.println("Vroom!");
      }
   };
   racecar.start();
}
System.out.println("Go");
countdown.countDown();   //all threads start now!

Вы также можете использовать это как "барьер" в стиле MPI, который заставляет все потоки ждать, пока другие потоки дойдут до определенной точки, прежде чем продолжить.

final CountDownLatch countdown = new CountDownLatch(num_thread);
for (int i = 0; i < num_thread; ++ i){
   Thread t= new Thread() {    
      public void run()    {
         doSomething();
         countdown.countDown();
         System.out.printf("Waiting on %d other threads.",countdown.getCount());
         countdown.await();     //waits until everyone reaches this point
         finish();
      }
   };
   t.start();
}

Чтобы все сказанное, защелку CountDown можно безопасно использовать так, как показано в вашем примере.

Ответ 2

CountDownLatch используется для запуска серии потоков, а затем дождаться, пока все они будут завершены (или пока они не назовут countDown() заданное количество раз.

Семафор используется для управления количеством параллельных потоков, которые используют ресурс. Этот ресурс может быть чем-то вроде файла или может быть процессором, ограничивая количество выполняемых потоков. Счет на Семафоре может идти вверх и вниз, поскольку разные потоки называют acquire() и release().

В вашем примере вы, по сути, используете Семафор как своего рода CountUPLatch. Учитывая, что вы намерены ждать завершения всех потоков, использование CountdownLatch делает ваше намерение более ясным.

Ответ 3

Краткое описание:

  • Semaphore и CountDownLatch служит для разных целей.

  • Используйте Семафор для управления доступом к ресурсам.

  • Используйте CountDownLatch для ожидания завершения всех потоков

Семафор определение из javadocs:

Семафор поддерживает набор разрешений. Каждый приобретать() блокирует, если необходимо, до получения разрешения, а затем берет его. Каждый release() добавляет разрешение, потенциально освобождая блокирующего покупателя.

Однако никаких реальных объектов разрешения не используются; Семафор просто подсчитывает количество доступных и действует соответственно.

Как это работает?

Семафоры используются для управления количеством параллельных потоков, которые используют ресурс. Этот ресурс может быть чем-то вроде общих данных или блоком кода (критический раздел) или любым файлом.

Счет на Семафоре может идти вверх и вниз, поскольку разные потоки называют acquire() и release(). Но в любой момент времени вы не можете увеличить число потоков больше, чем количество Семафор.

Семафор Варианты использования:

  • Ограничение одновременного доступа к диску (это может привести к конкурирующий диск ищет)
  • Ограничение создания темы
  • Объединение/ограничение пула JDBC
  • Управление сетевым подключением
  • Дросселирование задач с процессором или памятью

Посмотрите на эту статью для использования семафора.

Определение CountDownLatch из javadocs:

Помощник синхронизации, позволяющий одному или нескольким потокам ждать завершения набора операций в других потоках.

Как это работает?

CountDownLatch работает, имея счетчик, инициализированный количеством потоков, который уменьшается каждый раз, когда поток завершает его выполнение. Когда счетчик достигает нуля, это означает, что все потоки завершили выполнение, а ожидание потока на защелке возобновит выполнение.

CountDownLatch Случаи использования:

  • Достижение максимума Parallelism: Иногда мы хотим запустить несколько потоков в то же время для достижения максимального parallelism
  • Подождите N потоков до завершения перед запуском
  • Определение тупика.

Посмотрите на статью, чтобы четко понимать концепции CountDownLatch.

Посмотрите Fork Join Pool в этой статье тоже. Он имеет некоторое сходство с CountDownLatch.

Ответ 4

Скажите, что вы вошли в гольф-магазин, надеясь найти четверку,

Когда вы стоите в очереди, чтобы получить время от одного из продавцов про магазина, по сути, вы назвали proshopVendorSemaphore.acquire(), как только вы получите время tee, вы вызвали proshopVendorSemaphore.release(). Примечание: любой из бесплатных операторов может обслуживать вы, т.е. общий ресурс.

Теперь вы подходите к стартеру, он запускает CountDownLatch(4) и вызывает await(), чтобы ждать других, для вашей стороны вы вызывали check-in, т.е. CountDownLatch. countDown(), а также остальная часть foursome. Когда все приходят, стартер дает вперед (await() возвращает вызов)

Теперь, после девяти отверстий, когда каждый из вас делает перерыв, гипотетически позволяет снова задействовать стартер, он использует "новый" CountDownLatch(4), чтобы отключить Отверстие 10, такое же ожидание/синхронизация, как и отверстие 1.

Однако, если стартер использовал a CyclicBarrier для начала, он мог бы иметь reset тот же самый экземпляр в Отверстие 10 вместо второй защелки, которые используют и бросают.

Ответ 5

Глядя на свободно доступный источник, в реализации двух классов нет магии, поэтому их производительность должна быть одинаковой. Выберите тот, который делает ваше намерение более очевидным.

Ответ 6

CountdownLatch заставляет потоки ждать по методу wait() до тех пор, пока счетчик не достигнет нуля. Поэтому, возможно, вы хотите, чтобы все ваши потоки подождали до 3-х вызовов чего-то, тогда все потоки могут идти. Защелка вообще не может быть reset.

Семафор позволяет потокам получать разрешения, что предотвращает выполнение слишком большого количества потоков одновременно, блокируя, если он не может получить разрешение (ы), которое требуется для продолжения. Разрешения могут быть возвращены в Семафор, чтобы другие ожидающие потоки продолжались.