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

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

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

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

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

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



답변

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

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

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


답변

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

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

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

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

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


답변

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

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


답변

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

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

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

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

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


답변

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

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

소스 1.6 Javadoc


답변

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

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

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

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

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

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


답변

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

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

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