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

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

85 голосов
/ 08 октября 2008

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

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

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

Ответы [ 6 ]

101 голосов
/ 08 октября 2008

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

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

62 голосов
/ 09 октября 2008

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

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

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

14 голосов
/ 10 ноября 2015

Краткое резюме:

  1. Семафор и CountDownLatch служат для различных целей.

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

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

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

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

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

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

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

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

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

  1. Ограничение одновременного доступа к диску (это может снизить производительность из-за ищет конкурирующий диск)
  2. Ограничение создания темы
  3. Пул соединений JDBC / ограничение
  4. Регулирование сетевого подключения
  5. Дросселирование задач с интенсивным использованием процессора или памяти

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

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

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

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

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

CountDownLatch Примеры использования:

  1. Достижение максимального параллелизма: иногда мы хотим начать ряд потоки одновременно для достижения максимального параллелизма
  2. Ожидание завершения N потоков перед началом выполнения
  3. Обнаружение тупика.

Взгляните на эту статью , чтобы ясно понять концепции CountDownLatch.

Взгляните на Fork Join Pool и на эту статью . Он имеет некоторые сходства с CountDownLatch .

4 голосов
/ 02 августа 2013

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

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

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

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

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

1 голос
/ 08 октября 2008

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

0 голосов
/ 08 октября 2008

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

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

...