Java 동시성 : 카운트 다운 래치 대 순환 장벽


160

java.util.concurrent API를 읽고 있었고 ,

  • CountDownLatch: 다른 스레드에서 수행중인 작업 세트가 완료 될 때까지 하나 이상의 스레드가 대기 할 수 있도록하는 동기화 지원.
  • CyclicBarrier: 스레드 세트가 서로 공통 장벽 지점에 도달 할 때까지 기다릴 수있는 동기화 지원.

나에게 둘 다 똑같아 보이지만 더 많은 것이 있다고 확신합니다.

예를 들어에서 CoundownLatch, the countdown value could not be reset, that can happen in the case of CyclicBarrier.

둘 사이에 다른 차이점이 있습니까? 누군가 카운트 다운 값을 재설정하려는 위치
는 무엇입니까 use cases?


12
래치는 이벤트를 기다리기위한 것입니다. 장벽은 다른 스레드를 기다리는 것입니다. -Java Concurrency in Practice, B.Goetz et al.
user2418306

답변:


137

하나의 주요 차이점은 CyclicBarrier 가 공통 장벽 조건이 충족되면 실행되는 (선택적) 실행 가능 작업을 수행한다는 것입니다.

또한 장벽을 기다리는 클라이언트 수와 장벽을 트리거하는 데 필요한 수를 얻을 수 있습니다. 트리거되면 장벽이 재설정되고 다시 사용할 수 있습니다.

간단한 사용 사례-서비스 시작 등 ... CountdownLatch가 좋습니다. CyclicBarrier는보다 복잡한 조정 작업에 유용합니다. 이러한 일의 예로는 병렬 계산이 있는데, 여러 하위 작업이 MapReduce 와 같은 계산에 관여합니다 .


6
"또한 장벽을 기다리는 클라이언트의 수와 장벽을 트리거하는 데 필요한 수를 얻을 수 있습니다. 일단 트리거되면 장벽이 재설정되고 다시 사용될 수 있습니다." 나는이 점을 정말로 좋아한다. 내가 읽은 두 기사는 reset () 메서드를 호출하기 때문에 CyclicBarrier가 주기적이라는 것을 제안했습니다. 사실이지만 자주 언급하지 않는 것은 방벽이 트리거 되 자마자 자동으로 재설정된다는 것입니다. 이를 설명하기 위해 샘플 코드를 게시하겠습니다.
Kevin Lee

@Kevin Lee "방어벽이 작동하자마자 자동으로 재설정됩니다." 코드에서 reset ()을 호출 할 필요가 없습니다.
초신성

134

또 다른 차이점이 있습니다.

를 사용할 때 CyclicBarrier장벽을 트리거하는 대기 스레드 수를 지정한다고 가정합니다. 5를 지정하면 호출 할 스레드가 5 개 이상 있어야합니다 await().

를 사용할 때 대기중인 모든 스레드가 해제되는 CountDownLatch호출 횟수를 지정합니다 countDown(). 이것은 CountDownLatch하나의 스레드로만 사용할 수 있음을 의미합니다 .

"왜 그렇게 하시겠습니까?"라고 말할 수 있습니다. 콜백을 수행하는 다른 사람이 코딩 한 신비한 API를 사용한다고 상상해보십시오. 특정 콜백이 여러 번 호출 될 때까지 스레드 중 하나가 대기하기를 원합니다. 콜백이 어떤 스레드에서 호출 될지 모릅니다. 이 경우, a CountDownLatch는 완벽하지만 CyclicBarrier(실제로 할 수는 있지만 시간 초과가 발생합니다 ...)!

나는 그것이 CountDownLatch재설정 될 수 있기를 바랍니다 !


10
나는 이것이 이론상의 차이점을 더 잘 보여주는 답이라고 생각합니다. 장벽은 메소드를 여러 번 호출하여 래치를 깨뜨릴 수 있지만 장벽은 wait ()에 정확한 양의 스레드가 필요합니다.
flagg19

43
맞다-그것은 중요한 차이점이다 : CountDownLatch-> NumberOfCalls, CyclicBarrier-> NumberOfThreads
Ivan Voroshilin

1
필자는 CountDownLatch재설정 가능한 것이 좋겠다는 것에 동의합니다 . 거친 대기 알림을 구현하는 데 사용하는 해결 방법 CountDownLatch은 보호 된 코드 블록이 입력되면 (래치가 0에 도달 할 때) 즉시 새로 만드는 것 입니다. 이것은 물론 모든 상황 / 범위에 적용되지는 않지만 goldilocks 상황에서 옵션이라는 점에 주목할 가치가 있다고 생각했습니다.
Ephemera

2
이 주제에 대한 최고의 답변 중 하나입니다. Java Concurrency in Practice-같은 말 : Latches are for waiting for events; barriers are for waiting for other threads.. 이 두 가지의 차이점을 이해하기위한 기본적이고 필수적인 포인트.
Rahul Dev Mishra

Java 8 문서에 따르면 N으로 초기화 된 CountDownLatch를 사용하면 N 개의 스레드가 어떤 동작을 완료하거나 어떤 동작이 N 번 완료 될 때까지 한 스레드를 대기시킬 수 있습니다. 나에게 보인다 CountDownLatch를 -> NUMBEROFCALLS 또는 CountDownLatch를 -> NumberOfThreads
근적외선

41

아무도 언급하지 않은 한 가지 점은 CyclicBarrier에서 스레드에 문제가있는 경우 (시간 초과, 중단 ...), 도달 한 다른 모든 await()예외가 발생 한다는 것 입니다. Javadoc을 참조하십시오.

CyclicBarrier는 실패한 동기화 시도에 대해 전체 또는 없음 중단 모델을 사용합니다. 스레드가 중단, 실패 또는 시간 종료로 인해 차단 지점을 조기에 종료하면 해당 차단 지점에서 대기중인 다른 모든 스레드도 BrokenBarrierException (또는 InterruptedException)을 통해 비정상적으로 종료됩니다. 그들도 거의 동시에 중단 된 경우).


22

JavaDoc이 차이점을 명시 적으로 설명했다고 생각합니다. 대부분의 사람들은 CountDownLatch를 재설정 할 수 없지만 CyclicBarrier는 재설정 할 수 없다는 것을 알고 있습니다. 그러나 이것이 유일한 차이점은 아니거나 CyclicBarrier의 이름을 ResetbleCountDownLatch로 바꿀 수 있습니다. JavaDoc에 설명 된 목표의 관점에서 차이점을 알려야합니다.

CountDownLatch : 다른 스레드에서 수행중인 작업 집합이 완료 될 때까지 하나 이상의 스레드가 대기 할 수 있도록하는 동기화 지원.

CyclicBarrier : 일련의 스레드가 서로 공통 장벽 지점에 도달 할 때까지 기다릴 수있는 동기화 지원.

countDownLatch에는 다른 스레드 세트 가 완료 되기를 기다리는 하나 이상의 스레드가 있습니다 . 이 상황에는 두 가지 유형의 스레드가 있습니다. 한 유형은 대기 중이고 다른 유형은 무언가를하고 있습니다. 작업을 완료 한 후 대기 중이거나 종료 될 수 있습니다.

CyclicBarrier에는 한 가지 유형의 스레드 만 있으며 서로를 기다리고 있으며 동일합니다.


1
"CyclicBarrier에는 한 가지 유형의 스레드 만 있습니다."... 다른 스레드가 .await ()를 호출 할 때까지 "대기 역할"이 동일하지만 "작업이 동일하지 않을 수 있습니다". 또한 모두 동일한 유형 또는 다른 유형의 스레드 인스턴스 (!)가 절대적으로 달라야하지만 CountDownLatch에서는 동일한 스레드가 countDown ()을 호출하고 결과에 영향을 줄 수 있습니다.
블라디미르 나보코프

CountDownLatch에는 본질적으로 countDown 클라이언트 하나와 대기 클라이언트 하나라는 두 가지 역할이 필요하다는 데 동의합니다. 반면에 CyclicBarrier 클라이언트는 await 메소드만으로도 제대로 작동 할 수 있습니다.
isaolmez

14

주요 차이점은 Javadocs for CountdownLatch에 문서화되어 있습니다. 즉:

CountDownLatch는 주어진 횟수로 초기화됩니다. await 메소드는 countDown () 메소드의 호출로 인해 현재 카운트가 0에 도달 할 때까지 차단되며, 그 후 모든 대기 스레드가 해제되고 이후의 await 호출이 즉시 리턴됩니다. 이것은 일회성 현상이므로 카운트를 재설정 할 수 없습니다. 카운트를 재설정하는 버전이 필요한 경우 CyclicBarrier 사용을 고려하십시오.

소스 1.6 Javadoc


4
차이를 재설정 할 수있는 경우 CyclicBarrier의 이름을 ResetableCountDownLatch로 지정하는 것이 더 좋을 수 있습니다. 이는 차이로 인해 더 의미가 있습니다.
James.Xu

12

CountDownLatch는 일회성 동기화에 사용됩니다. CountDownLatch를 사용하는 동안 모든 스레드는 원하는만큼 countDown ()을 호출 할 수 있습니다. await ()를 호출 한 스레드는 다른 차단되지 않은 스레드에 의한 countDown () 호출로 인해 카운트가 0에 도달 할 때까지 차단됩니다. 해, CountDownLatch에 대한 javadoc의 상태 :

await 메소드는 countDown () 메소드의 호출로 인해 현재 카운트가 0에 도달 할 때까지 차단되며, 그 후 모든 대기 스레드가 해제되고 이후의 await 호출이 즉시 리턴됩니다. ...

또 다른 일반적인 사용법은 문제를 N 부분으로 나누고 해당 부분을 실행하고 래치에서 카운트 다운하는 Runnable을 사용하여 각 부분을 설명하고 모든 Runnable을 Executor에 대기시키는 것입니다. 모든 하위 부품이 완료되면 조정 스레드가 대기를 통과 할 수 있습니다. 스레드가 이런 식으로 반복적으로 카운트 다운해야하는 경우 CyclicBarrier를 사용하십시오.

반대로 순환 장벽은 여러 동기화 지점에 사용됩니다 (예 : 스레드 세트가 루프 / 위상 계산을 실행 중이고 다음 반복 / 단계를 시작하기 전에 동기화해야하는 경우). CyclicBarrierjavadoc에 따라 :

대기 스레드가 해제 된 후 재사용 할 수 있으므로 장벽을 순환이라고합니다.

CountDownLatch와 달리 await ()에 대한 각 호출은 특정 단계에 속하며 해당 단계에 속하는 모든 당사자가 await ()를 호출 할 때까지 스레드가 차단 될 수 있습니다. CyclicBarrier가 지원하는 명시적인 countDown () 작업이 없습니다.


12

이 질문은 이미 적절하게 답변되었지만 코드를 게시하여 약간의 가치를 추가 할 수 있다고 생각합니다.

순환 장벽의 동작을 설명하기 위해 샘플 코드를 만들었습니다. 배리어가 밀리 자마자 다시 사용할 수 있도록 배리어가 자동 재설정됩니다 (따라서 "사이 클릭"). 프로그램을 실행할 때, 장벽을 두드린 후에 만 ​​"출력을하자"인쇄물이 출력되는 것을 관찰하십시오.

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's play.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}

8

래치와 순환 장벽에 대해 공부할 때 나는이 은유를 생각해 냈습니다. 순환 장벽 : 회사에 회의실이 있다고 상상해보십시오. 회의를 시작하려면 특정 수의 회의 참석자가 회의에 참석해야합니다 (공식으로 만들기). 다음은 일반 회의 참석자 (직원)의 코드입니다.

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!>

다른 외부 스레드 (지진)가 회의를 취소하는 또 다른 시나리오가 있습니다 (통화 재설정 방법). 이 경우 모든 대기 스레드가 예외로 깨어납니다.

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 ...");
 }
}

래치 : 화난 상사가 회사 고객을 위해 전시회를 열려면 모든 준비가 완료되어야합니다 (자원). 우리는 모든 작업자 (스레드)가 할 일 목록을 제공하고 할 일 목록을 확인합니다 (일부 작업자는 그림을 작성하고 다른 작업자는 사운드 시스템을 준비합니다 ...). 할 일 목록의 모든 항목이 완료되면 (자원 제공) 고객에게 문을 열 수 있습니다.

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();

7

간단히 말해서 , 둘 사이의 주요 기능적 차이점 을 이해 하십시오.

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하면서 (내가 다른 방법 일 수 있거나 AQS를 사용하거나 둘 다 성능 효율성을 잃지 않고 Lock을 사용할 수 있다는 의혹)CyclicBarrierReentrantLock


5

한 가지 분명한 차이점은 한 사이클에서 N의 CyclicBarrier에서 N 스레드 만 해제 될 수 있다는 것입니다. 그러나 CountDownLatch N에서 무제한 스레드 수를 기다릴 수 있습니다. 카운트 다운 감소는 한 스레드 N 씩 또는 N 스레드마다 하나씩 또는 조합하여 수행 할 수 있습니다.


4

CyclicBarrier의 경우 모든 하위 스레드가 barrier.await ()를 호출하기 시작하면 바로 Runnable이 Barrier에서 실행됩니다. 각 자식 스레드에서 barrier.await는 완료하는 데 다른 시간이 걸리며 모두 동시에 완료됩니다.


4

에서 CountDownLatch를 , 다른 스레드 메인 스레드 대기는 실행을 완료합니다. 년 으로 CyclicBarrier , 작업자 스레드는 실행을 완료하기 위해 서로 기다립니다.

카운트가 0에 도달하고 래치가 열리면 동일한 CountDownLatch 인스턴스를 재사용 할 수 없습니다. 반면에 장벽이 끊어지면 배리어를 재설정하여 CyclicBarrier 를 재사용 할 수 있습니다.


메인 스레드 일 필요는 없습니다. CountDownLatch를 작성하고 다른 비 주요 스레드와 공유하는 스레드 일 수 있습니다.
Aniket Thakur

1

CountDownLatch는 모든 것의 카운트 다운입니다. CyclicBarrier는 스레드에 대해서만 카운트 다운입니다.

작업자 스레드 5 개와 배송 스레드 1 개가 있다고 가정하고 작업자가 100 개의 품목을 생산할 때 배송 업체에서 배송합니다.

CountDownLatch의 경우 카운터는 작업자 또는 항목에있을 수 있습니다.

CyclicBarrier의 경우 카운터는 작업자 만 할 수 있습니다

작업자가 품목에 CountDownLatch를 포함하여 무한 수면 상태가되면 배송 업체는 배송 할 수 있습니다. 그러나 CyclicBarrier를 사용하면 배송 업체를 호출 할 수 없습니다


0

@Kevin Lee와 @Jon은 Optional Runnable을 사용하여 CyclicBarrier를 사용해 보았습니다. CyclicBarrier를 시작한 후 시작한 후 실행되는 것 같습니다. 코드와 출력은 다음과 같습니다

정적 사이 클릭 배리어 장벽;

    public static void main(String[] args) throws InterruptedException {
        barrier = new CyclicBarrier(3, new Runnable() {
            @Override
            public void run() {
                System.out.println("I run in the beginning and after the CyclicBarrier is tipped");
            }
        });

        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();
    }

산출

I run in the beginning and after the CyclicBarrier is tipped
Let's play.
Let's play.
Let's play.
Barrier automatically resets.
I run in the beginning and after the CyclicBarrier is tipped
Let's play.
Let's play.
Let's play.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.