CountDownLatch 대 세마포


92

사용의 이점이 있습니까?

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 번 경우를 제외하고는 래치를 재사용 할 수 없으며 더 중요한 것은 생성 될 스레드 수를 미리 알아야합니다 (또는 래치를 생성하기 전에 모두 시작될 때까지 기다려야 함).

그렇다면 어떤 상황에서 래치가 더 좋을까요?

답변:


109

CountDown 래치는 예제와 정반대로 자주 사용됩니다. 일반적으로 countown이 0에 도달하면 동시에 시작되는 "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 래치는 예제에서 보여준 방식으로 안전하게 사용할 수 있습니다.


1
감사. 따라서 여러 스레드가 래치에서 기다릴 수 있다면 내 두 가지 예는 동일하지 않습니다. sem.acquire (num_threads); sem.release (num_threads) ;? 나는 그것들이 다시 동등하게 될 것이라고 생각합니다.
finnw

어떤 의미에서, 예, 모든 스레드가 획득 한 후 해제되는 한 가능합니다. 엄밀히 말하면 아닙니다. 래치를 사용하면 모든 스레드를 동시에 시작할 수 있습니다. 세마포어를 사용하면 차례로 자격이 부여됩니다 (다른 스레드 스케줄링이 발생할 수 있음).
James Schek

Java 문서는이 CountdownLatch가 그의 예제와 잘 맞는 것을 암시하는 것 같습니다 : docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/… . 특히, "N으로 초기화 된 CountDownLatch는 N 스레드가 어떤 작업을 완료하거나 어떤 작업이 N 번 완료 될 때까지 하나의 스레드가 대기하도록 만드는 데 사용할 수 있습니다."
크리스 모리스

당신이 옳습니다. 나는 이것이 내가 본 CountDownLatch의 가장 일반적인 용도와 의도 된 용도임을 반영하기 위해 내 대답을 약간 업데이트 할 것입니다.
James Schek 2013

11
이것은 CountDownLatch를 가장 자주 사용하는 것은 무엇입니까? 세마포어를 통해 CountDownLatch를 사용할 때의 장점 / 차이점에 관한 원래 질문에 대한 답변이 아닙니다.
Marco Lackovic 2013

67

CountDownLatch 는 일련의 스레드를 시작한 다음 모든 스레드가 완료 될 때까지 (또는 countDown()지정된 횟수만큼 호출 될 때까지 대기하는 데 사용됩니다 .

세마포는 리소스를 사용하는 동시 스레드 수를 제어하는 ​​데 사용됩니다. 해당 리소스는 파일과 같은 것이거나 실행중인 스레드 수를 제한하여 CPU가 될 수 있습니다. 세마포어의 카운트는 다른 스레드가 acquire()release().

귀하의 예에서는 본질적으로 Semaphore를 일종의 Count UP Latch로 사용하고 있습니다. 당신의 의도가 모든 스레드가 끝날 때까지 기다리는 것이므로를 사용하면 CountdownLatch의도가 더 명확 해집니다.


22

짧은 요약:

  1. SemaphoreCountDownLatch 는 다른 용도로 사용됩니다.

  2. Semaphore 를 사용 하여 리소스에 대한 스레드 액세스를 제어합니다.

  3. CountDownLatch 를 사용 하여 모든 스레드가 완료 될 때까지 기다립니다.

javadocs의 세마포어 정의 :

세마포는 허가 세트를 유지한다. 각 acquire ()허가 를 사용할 수 있을 때까지 필요한 경우 차단 한 다음 가져옵니다. 각 release () 는 허가를 추가하여 잠재적으로 차단 취득자를 해제합니다.

그러나 실제 허용 개체는 사용되지 않습니다. 세마포어는 바로 사용할 수있는 수의 수를 유지하고 그에 따라 역할을합니다.

어떻게 작동합니까?

세마포는 리소스를 사용하는 동시 스레드 수를 제어하는 ​​데 사용됩니다. 해당 리소스는 공유 데이터, 코드 블록 ( 중요 섹션 ) 또는 파일 과 같은 것일 수 있습니다 .

세마포어의 카운트는 다른 스레드가 acquire() 및 release()를 호출함에 따라 증가 및 감소 할 수 있습니다 . 그러나 어느 시점에서든 세마포어 수보다 많은 수의 스레드를 가질 수 없습니다.

세마포 사용 사례 :

  1. 디스크에 대한 동시 액세스 제한 (경쟁하는 디스크 탐색으로 인해 성능이 저하 될 수 있음)
  2. 스레드 생성 제한
  3. JDBC 연결 풀링 / 제한
  4. 네트워크 연결 조절
  5. CPU 또는 메모리 집약적 인 작업 제한

세마포어 사용에 대한 이 기사 를 살펴보십시오 .

javadocs의 CountDownLatch 정의 :

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

어떻게 작동합니까?

CountDownLatch 는 스레드가 실행을 완료 할 때마다 감소하는 스레드 수로 카운터를 초기화하여 작동합니다. count가 0에 도달하면 모든 스레드가 실행을 완료하고 래치에서 대기중인 스레드가 실행을 다시 시작 함을 의미합니다.

CountDownLatch 사용 사례 :

  1. 최대 병렬 처리 달성 : 때로는 최대 병렬 처리를 달성하기 위해 동시에 여러 스레드를 시작하려고합니다.
  2. 실행을 시작하기 전에 N 스레드가 완료 될 때까지 기다립니다.
  3. 교착 상태 감지.

CountDownLatch 개념을 명확하게 이해하려면 이 기사 를 살펴보십시오 .

기사 에서도 Fork Join Pool 을 살펴보십시오 . CountDownLatch 와 몇 가지 유사점이 있습니다.


7

포섬을 찾고 싶어 골프 프로 샵에 들어갔다고 가정 해 보겠습니다.

프로 샵 직원 중 한 명으로부터 티타임을 받기 위해 줄을 서면 기본적으로에게 전화 proshopVendorSemaphore.acquire()를했고, 티타임을 proshopVendorSemaphore.release()받으면 에게 전화를 걸었습니다. 참고 : 무료 직원이 서비스를 제공 할 수 있습니다.

이제 시작으로 걸어 가면 그는 a를 시작하고 다른 사람을 기다리 CountDownLatch(4)라고 전화 await()합니다 CountDownLatch. countDown()나머지 포섬도 마찬가지입니다. 모두 도착하면 스타터는 계속 진행합니다 ( await()콜 리턴)

이제 9 홀 후에 각자 휴식을 취하면 가상적으로 스타터를 다시 투입 할 수 있습니다. 그는 'new' CountDownLatch(4)를 사용하여 홀 10을 티 오프합니다. 홀 1과 동일한 대기 / 동기화입니다.

그러나 스타터가 a CyclicBarrier를 사용 하여 시작했다면 & throw를 사용하는 두 번째 래치 대신 홀 10에서 동일한 인스턴스를 재설정 할 수 있습니다.


1
귀하의 답변을 이해하고 있는지 확실하지 않지만 CountdownLatch 및 Semaphore가 어떻게 작동하는지 설명하려는 경우 질문의 주제가 아닙니다.
finnw

10
불행히도 나는 골프에 대해 아무것도 모릅니다.
반지 전달자

그러나 스타터 작업은 .acquire (players)를 사용하여 수행 할 수 있으며 릴리스와 함께 관련 횟수를 늘릴 수 있습니다. 카운트 다운 래치는 기능이 적고 재사용 성이없는 것 같습니다.
라시 킨 누넨

1

자유롭게 사용할 수있는 소스를 살펴보면 두 클래스의 구현에 마법이 없으므로 성능이 거의 동일해야합니다. 당신의 의도를 더 분명하게 만드는 것을 선택하십시오.


0

CountdownLatchawait()카운트가 0에 도달 할 때까지 스레드가 메서드에서 대기하도록합니다 . 따라서 모든 스레드가 무언가를 3 번 ​​호출 할 때까지 기다리면 모든 스레드가 진행될 수 있습니다. A는 Latch일반적으로 재설정 할 수 없습니다.

A Semaphore는 스레드가 허가를 검색 할 수 있도록하여 너무 많은 스레드가 한 번에 실행되는 것을 방지하고 진행에 필요한 허가를 얻을 수없는 경우 차단합니다. Semaphore다른 대기 스레드가 진행할 수 있도록 허용을로 반환 할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.