Java concurrency: обратный отсчет против циклического барьера

Я читал java.util.concurrent API и обнаружил, что

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

Для меня оба кажутся равными, но я уверен, что в этом есть гораздо больше.

Например, в CoundownLatch, the countdown value could not be reset, that can happen in the case of CyclicBarrier.

Есть ли другая разница между ними?
Что такое use cases, где кто-то захочет reset значение обратного отсчета?

Ответ 1

Одно существенное отличие состоит в том, что CyclicBarrier принимает (необязательную) задачу Runnable, которая запускается после выполнения общего условия барьера.

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

Для простых случаев использования - запуск сервисов и т.д. CountdownLatch в порядке. CyclicBarrier полезен для более сложных задач координации. Примером такой вещи может быть параллельное вычисление - где в подсчете участвуют несколько подзадач, вроде MapReduce.

Ответ 2

Есть и другая разница.

При использовании CyclicBarrier предполагается, что вы указываете количество ожидающих потоков, которые запускают барьер. Если вы укажете 5, для вызова await() должно быть не менее 5 потоков.

При использовании CountDownLatch вы указываете количество вызовов на countDown(), что приведет к освобождению всех ожидающих потоков. Это означает, что вы можете использовать CountDownLatch только с одним потоком.

"Зачем вам это делать?", вы можете сказать. Представьте, что вы используете таинственный API, закодированный кем-то другим, который выполняет обратные вызовы. Вы хотите, чтобы один из ваших потоков подождал, пока какой-то callback был вызван несколько раз. Вы не знаете, какие потоки будут вызваны обратным вызовом. В этом случае a CountDownLatch совершенен, тогда как я не могу придумать какой-либо способ реализовать это, используя CyclicBarrier (на самом деле, я могу, но он включает тайм-ауты... yuck!).

Я просто хочу, чтобы CountDownLatch мог быть reset!

Ответ 3

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

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

Ответ 4

Я думаю, что JavaDoc явно объяснил различия. Большинство людей знают, что CountDownLatch не может быть reset, однако CyclicBarrier может. Но это не единственная разница, или CyclicBarrier можно переименовать в ResetbleCountDownLatch. Мы должны рассказать о различиях с точки зрения их целей, которые описаны в JavaDoc

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

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

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

В CyclicBarrier существует только один тип потоков, они ждут друг друга, они равны.

Ответ 5

Основное различие зафиксировано в Javadocs для CountdownLatch. А именно:

Функция CountDownLatch инициализируется данный счет. Блок ожидания пока текущий счет не достигнет нуля из-за вызовов countDown() метод, после которого все ожидания потоки освобождаются и любые последующие призывы ожидают возвращения немедленно. Это одноразовый явление - счет не может быть reset. Если вам нужна версия, которая сбрасывает счет, рассмотрите возможность использования CyclicBarrier.

источник 1.6 Javadoc

Ответ 6

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

Чтобы проиллюстрировать поведение циклического барьера, я сделал несколько примеров кода. Как только барьер будет опрокинут, он автоматически будет reset, чтобы его можно было использовать снова (следовательно, он является "циклическим" ). Когда вы запускаете программу, обратите внимание, что выходы "Let play" срабатывают только после того, как барьер опрокинут.

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierCycles {

    static CyclicBarrier barrier;

    public static void main(String[] args) throws InterruptedException {
        barrier = new CyclicBarrier(3); 

        new Worker().start();
        Thread.sleep(1000);
        new Worker().start();
        Thread.sleep(1000);
        new Worker().start();
        Thread.sleep(1000);

        System.out.println("Barrier automatically resets.");

        new Worker().start();
        Thread.sleep(1000);
        new Worker().start();
        Thread.sleep(1000);
        new Worker().start();
    }

}


class Worker extends Thread {
    @Override
    public void run() {
        try {
            CyclicBarrierCycles.barrier.await();
            System.out.println("Let play.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

Ответ 7

CountDownLatch используется для одноразовой синхронизации. При использовании CountDownLatch любому потоку разрешено вызывать countDown() столько раз, сколько им нравится. Темы, вызывающие await(), блокируются до тех пор, пока счетчик не достигнет нуля из-за вызовов countDown() другими разблокированными потоками. В javadoc для CountDownLatch говорится:

Ожидающие методы блокируют до тех пор, пока текущий счет не достигнет нуля из-за invocations метода countDown(), после которого все ожидающие потоки освобождаются и любые последующие призывы ожидают возвращения немедленно....

Еще одно типичное использование - разделить проблему на N частей, описать каждую часть с помощью Runnable, которая выполняет эту часть и рассчитывает на защелку и ставит в очередь все Runnables для Исполнителя. Когда все части будут завершены, координационная нить сможет пройти через ожидание. (Когда потоки должны многократно обращать внимание на таким образом, вместо этого используйте CyclicBarrier.)

Напротив, циклический барьер используется для множества точек синхронизации, например. если в наборе потоков выполняется цикл/поэтапное вычисление и требуется синхронизация перед началом следующей итерации/фазы. В соответствии с javadoc для CyclicBarrier:

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

В отличие от CountDownLatch, каждый вызов await() принадлежит к некоторой фазе и может вызвать блокировку потока, пока все стороны, принадлежащие этой фазе, не вызовут wait(). Нет явной операции countDown(), поддерживаемой CyclicBarrier.

Ответ 8

В случае CyclicBarrier, как только ВСЕ дочерние потоки начнут вызывать барьер .await(), Runnable выполняется в Barrier. Барьер.авита в каждой дочерней нити займет много времени, чтобы закончить, и все они заканчиваются одновременно.

Ответ 9

Одно очевидное отличие состоит в том, что только N потоков могут ждать на CyclicBarrier из N, чтобы быть выпуском за один цикл. Но неограниченное количество потоков может ждать на CountDownLatch N. Уменьшение обратного отсчета может выполняться одним потоком N раз или N потоков один раз каждый или комбинациями.

Ответ 10

Вкратце, просто чтобы понять ключевые функциональные различия между ними:

public class CountDownLatch {
    private Object mutex = new Object();
    private int count;

    public CountDownLatch(int count) {
        this.count = count;
    }

    public void await() throws InterruptedException {
        synchronized (mutex) {
            while (count > 0) {
                mutex.wait();
            }
        }
    }

    public void countDown() {
        synchronized (mutex) {
            if (--count == 0)
                mutex.notifyAll();
        }

    }
}

и

public class CyclicBarrier {
    private Object mutex = new Object();
    private int count;

    public CyclicBarrier(int count) {
        this.count = count;
    }

    public void await() throws InterruptedException {
        synchronized (mutex) {
            count--;
            while(count > 0)
                mutex.wait();
            mutex.notifyAll();
        }
    }
}

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

Вышеприведенные классы, однако, полностью функциональны и эквивалентны в рамках предоставленной функциональности их соответствующим тезкам.

В другом примечании CountDownLatch подклассы внутреннего класса AQS, тогда как CyclicBarrier использует ReentrantLock (мое подозрение в том, что это может быть наоборот, или оба могут использовать AQS или оба используют Lock - без каких-либо потерь эффективности работы)

Ответ 11

В CountDownLatch основные потоки ждут, пока другие потоки завершат выполнение. В CyclicBarrier рабочие потоки ждут друг друга, чтобы завершить выполнение.

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

Ответ 12

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

class MeetingAtendee implements Runnable {

CyclicBarrier myMeetingQuorumBarrier;

public MeetingAtendee(CyclicBarrier myMileStoneBarrier) {
    this.myMeetingQuorumBarrier = myMileStoneBarrier;
}

@Override
public void run() {
    try {
        System.out.println(Thread.currentThread().getName() + " i joined the meeting ...");
        myMeetingQuorumBarrier.await();
        System.out.println(Thread.currentThread().getName()+" finally meeting stared ...");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        System.out.println("Meeting canceled! every body dance <by chic band!>");
    }
 }
}
Сотрудник

присоединяется к собранию, ждет, когда другие придут, чтобы начать собрание. также он уходит, если собрание отменяется:), тогда у нас есть БОСС, как доза не нравится ждать, пока другие появятся, и если он потеряет своего пациента, он отменяет встречу.

class MeetingAtendeeTheBoss implements Runnable {

CyclicBarrier myMeetingQuorumBarrier;

public MeetingAtendeeTheBoss(CyclicBarrier myMileStoneBarrier) {
    this.myMeetingQuorumBarrier = myMileStoneBarrier;
}

@Override
public void run() {
    try {
        System.out.println(Thread.currentThread().getName() + "I am THE BOSS - i joined the meeting ...");
        //boss dose not like to wait too much!! he/she waits for 2 seconds and we END the meeting
        myMeetingQuorumBarrier.await(1,TimeUnit.SECONDS);
        System.out.println(Thread.currentThread().getName()+" finally meeting stared ...");
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (BrokenBarrierException e) {
        System.out.println("what WHO canceled The meeting");
    } catch (TimeoutException e) {
        System.out.println("These employees waste my time!!");
    }
 }
}

В обычный день работник приходит на встречу, ожидая появления другого, и если некоторые участники не приходят, им приходится ждать бесконечно! на какой-то специальной встрече приходит босс, и ему не нравится ждать. (5 человек должны начать встречаться, но приходит только босс, а также восторженный сотрудник), поэтому он отменяет встречу (сердито)

CyclicBarrier meetingAtendeeQuorum = new CyclicBarrier(5);
Thread atendeeThread = new Thread(new MeetingAtendee(meetingAtendeeQuorum));
Thread atendeeThreadBoss = new Thread(new MeetingAtendeeTheBoss(meetingAtendeeQuorum));
    atendeeThread.start();
    atendeeThreadBoss.start();

Вывод:

//Thread-1I am THE BOSS - i joined the meeting ...
// Thread-0 i joined the meeting ...
// These employees waste my time!!
// Meeting canceled! every body dance <by chic band!>

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

class NaturalDisasters implements Runnable {

CyclicBarrier someStupidMeetingAtendeeQuorum;

public NaturalDisasters(CyclicBarrier someStupidMeetingAtendeeQuorum) {
    this.someStupidMeetingAtendeeQuorum = someStupidMeetingAtendeeQuorum;
}

void earthQuakeHappening(){
    System.out.println("earth quaking.....");
    someStupidMeetingAtendeeQuorum.reset();
}

@Override
public void run() {
    earthQuakeHappening();
 }
}

код запуска приведет к смешному выводу:

// Thread-1I am THE BOSS - i joined the meeting ...
// Thread-0 i joined the meeting ...
// earth quaking.....
// what WHO canceled The meeting
// Meeting canceled! every body dance <by chic band!>

Вы также можете добавить секретаря в конференц-зал, если будет проведена встреча, она будет документировать все, но она не входит в собрание:

class MeetingSecretary implements Runnable {

@Override
public void run() {
        System.out.println("preparing meeting documents");
        System.out.println("taking notes ...");
 }
}

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

public class Visitor implements Runnable{

CountDownLatch exhibitonDoorlatch = null;

public Visitor (CountDownLatch latch) {
    exhibitonDoorlatch  = latch;
}

public void run() {
    try {
        exhibitonDoorlatch .await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("customer visiting exebition");
 }
}

И рабочие, как готовят выставку:

class Worker implements Runnable {

CountDownLatch myTodoItem = null;

public Worker(CountDownLatch latch) {
    this.myTodoItem = latch;
}

public void run() {
        System.out.println("doing my part of job ...");
        System.out.println("My work is done! remove it from todo list");
        myTodoItem.countDown();
 }
}

    CountDownLatch preperationTodoList = new CountDownLatch(3);

    // exhibition preparation workers  
    Worker      electricalWorker      = new Worker(preperationTodoList);
    Worker      paintingWorker      = new Worker(preperationTodoList);

    // Exhibition Visitors 
    ExhibitionVisitor exhibitionVisitorA = new ExhibitionVisitor(preperationTodoList);
    ExhibitionVisitor exhibitionVisitorB = new ExhibitionVisitor(preperationTodoList);
    ExhibitionVisitor exhibitionVisitorC = new ExhibitionVisitor(preperationTodoList);

    new Thread(electricalWorker).start();
    new Thread(paintingWorker).start();

    new Thread(exhibitionVisitorA).start();
    new Thread(exhibitionVisitorB).start();
    new Thread(exhibitionVisitorC).start();