Обратный отсчет против семафора


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

Ява.утиль.параллельный.Обратный отсчет

Вместо

Ява.утиль.параллельный.Семафор?

Насколько я могу судить, следующие фрагменты почти эквивалентны:

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: обратный отсчет

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 81

6 ответов:

Защелка обратного отсчета часто используется для полной противоположности вашему примеру. Как правило, у вас будет много потоков, блокирующих "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();
}

Что все сказанное, защелка обратного отсчета может безопасно использоваться так, как вы показали в своем образец.

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

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

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

краткое содержание:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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