[java] CountDownLatch는 Java 멀티 스레딩에서 어떻게 사용됩니까?

누군가 Java CountDownLatch가 무엇인지 언제 사용 하는지 이해하도록 도와 줄 수 있습니까 ?

이 프로그램의 작동 방식에 대한 명확한 아이디어가 없습니다. 세 개의 스레드가 모두 한 번에 시작되고 각 스레드가 3000ms 후에 CountDownLatch를 호출한다는 것을 이해합니다. 카운트 다운은 하나씩 감소합니다. 래치가 0이되면 프로그램은 “Completed”를 인쇄합니다. 어쩌면 내가 이해 한 방식이 틀릴 수도 있습니다.

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Processor implements Runnable {
    private CountDownLatch latch;

    public Processor(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        System.out.println("Started.");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        latch.countDown();
    }
}

// ———————————————— —–

public class App {

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(3); // coundown from 3 to 0

        ExecutorService executor = Executors.newFixedThreadPool(3); // 3 Threads in pool

        for(int i=0; i < 3; i++) {
            executor.submit(new Processor(latch)); // ref to latch. each time call new Processes latch will count down by 1
        }

        try {
            latch.await();  // wait until latch counted down to 0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Completed.");
    }

}



답변

예, 당신은 올바르게 이해했습니다.
CountDownLatch래치 원리로 작동하면 주 스레드는 게이트가 열릴 때까지 기다립니다. 에 대한 하나 개의 스레드 대기 n 개의 을 만드는 동안 지정된 스레드 CountDownLatch.

CountDownLatch.await()카운트는 0에 도달하거나 다른 스레드에 의해 중단 될 때까지 호출하는 모든 스레드 (일반적으로 응용 프로그램의 기본 스레드)입니다 . 다른 모든 스레드는 CountDownLatch.countDown()완료되거나 준비되면 호출하여 카운트 다운해야합니다 .

카운트가 0에 도달하면 대기 스레드가 계속됩니다. 단점 / 장점 중 하나는 CountDownLatch재사용 할 수 없다는 것 CountDownLatch입니다. 카운트가 0에 도달하면 더 이상 사용할 수 없습니다 .

편집하다:

CountDownLatch하나의 스레드 (예 : 기본 스레드)가 처리를 계속하기 전에 하나 이상의 스레드가 완료 될 때까지 기다려야 할 때 사용하십시오 .

CountDownLatchJava에서 사용하는 전형적인 예 는 서비스 아키텍처를 사용하는 서버 측 핵심 Java 응용 프로그램으로, 여러 스레드에서 여러 서비스를 제공하고 모든 서비스가 성공적으로 시작될 때까지 응용 프로그램이 처리를 시작할 수 없습니다.

PS OP의 질문은 꽤 간단한 예가 있으므로 포함하지 않았습니다.


답변

CountDownLatchJava에서 처리기 시작하기 전에 Thread 하나 이상의 Threads 를 기다릴 수있는 동기화 프로그램 유형입니다 .

CountDownLatch래치 원리로 작동하면 게이트가 열릴 때까지 스레드가 대기합니다. 하나의 스레드는 n작성하는 동안 지정된 스레드 수를 기다립니다 CountDownLatch.

예 : final CountDownLatch latch = new CountDownLatch(3);

여기서 카운터를 3으로 설정했습니다.

모든 스레드 호출 응용 프로그램의 보통 메인 쓰레드, CountDownLatch.await()카운트 0에 도달하거나 다른 의해 중단 될 때까지 기다립니다 Thread. 다른 모든 스레드는 CountDownLatch.countDown()완료되거나 작업 준비가되면 호출하여 카운트 다운 해야합니다. 카운트가 0에 도달하면 Thread대기가 시작됩니다.

여기서 카운트는 CountDownLatch.countDown()방법에 따라 감소합니다 .

Thread부르는있는 await()방법은 제로 초기 카운트에 도달 할 때까지 기다립니다.

다른 스레드 수를 0으로 만들려면 countDown()메서드 를 호출해야합니다 . 카운트가 0이되면 await()메소드 를 호출 한 스레드 가 재개됩니다 (실행 시작).

단점은 CountDownLatch재사용 할 수 없다는 것입니다. 카운트가 0이되면 더 이상 사용할 수 없습니다.


답변

NikolaB는 그것을 잘 설명했지만 예제는 이해하는 데 도움이 될 것이므로 간단한 예제가 있습니다.

 import java.util.concurrent.*;


  public class CountDownLatchExample {

  public static class ProcessThread implements Runnable {

    CountDownLatch latch;
    long workDuration;
    String name;

    public ProcessThread(String name, CountDownLatch latch, long duration){
        this.name= name;
        this.latch = latch;
        this.workDuration = duration;
    }


    public void run() {
        try {
            System.out.println(name +" Processing Something for "+ workDuration/1000 + " Seconds");
            Thread.sleep(workDuration);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+ "completed its works");
        //when task finished.. count down the latch count...

        // basically this is same as calling lock object notify(), and object here is latch
        latch.countDown();
    }
}


public static void main(String[] args) {
    // Parent thread creating a latch object
    CountDownLatch latch = new CountDownLatch(3);

    new Thread(new ProcessThread("Worker1",latch, 2000)).start(); // time in millis.. 2 secs
    new Thread(new ProcessThread("Worker2",latch, 6000)).start();//6 secs
    new Thread(new ProcessThread("Worker3",latch, 4000)).start();//4 secs


    System.out.println("waiting for Children processes to complete....");
    try {
        //current thread will get notified if all chidren's are done 
        // and thread will resume from wait() mode.
        latch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("All Process Completed....");

    System.out.println("Parent Thread Resuming work....");



     }
  }


답변

하나 이상의 스레드가 작업을 완료하기를 기다릴 때 사용됩니다. 스레드에서 조인하는 것과 비슷합니다.

CountDownLatch를 사용할 수있는 곳

세 개의 스레드 “A”, “B”및 “C”가 있고 “A”및 “B”스레드가 작업을 완료하거나 부분적으로 완료 할 때만 스레드 “C”를 시작하려는 시나리오가 있다고 가정합니다.

실제 IT 시나리오에 적용 가능

관리자가 개발 팀 (A와 B)간에 모듈을 나누고 두 팀이 작업을 완료 한 경우에만 테스트를 위해 QA 팀에 모듈을 할당하려는 시나리오를 고려하십시오.

public class Manager {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        MyDevTeam teamDevA = new MyDevTeam(countDownLatch, "devA");
        MyDevTeam teamDevB = new MyDevTeam(countDownLatch, "devB");
        teamDevA.start();
        teamDevB.start();
        countDownLatch.await();
        MyQATeam qa = new MyQATeam();
        qa.start();
    }
}

class MyDevTeam extends Thread {
    CountDownLatch countDownLatch;
    public MyDevTeam (CountDownLatch countDownLatch, String name) {
        super(name);
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        System.out.println("Task assigned to development team " + Thread.currentThread().getName());
        try {
                Thread.sleep(2000);
        } catch (InterruptedException ex) {
                ex.printStackTrace();
        }
    System.out.println("Task finished by development team Thread.currentThread().getName());
            this.countDownLatch.countDown();
    }
}

class MyQATeam extends Thread {
    @Override
    public void run() {
        System.out.println("Task assigned to QA team");
        try {
                Thread.sleep(2000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("Task finished by QA team");
    }
}

위 코드의 출력은 다음과 같습니다.

개발 팀 devB에 할당 된 작업

개발 팀 devA에 할당 된 작업

개발 팀 devB에 의해 완료된 작업

개발 팀 devA에 의해 완료된 작업

품질 보증팀에 할당 된 작업

품질 관리팀에서 작업 완료

여기서 await () 메소드는 countdownlatch 플래그가 0이 될 때까지 대기하고 countDown () 메소드는 countdownlatch 플래그를 1 씩 감소시킵니다.

JOIN의 제한 :
위의 예는 JOIN으로 도 달성 할 수 있지만 JOIN은 두 가지 시나리오에서 사용할 수 없습니다.

  1. Thread 클래스 대신 ExecutorService를 사용하여 스레드를 만드는 경우
  2. 개발이 80 % 작업을 완료하자마자 Manager가 QA 팀에게 코드를 전달하려는 위의 예를 수정하십시오. CountDownLatch를 사용하면 부분 실행을 위해 다른 스레드를 기다리는 데 사용할 수있는 구현을 수정할 수 있습니다.

답변

CoundDownLatch를 사용하면 다른 모든 스레드가 실행으로 완료 될 때까지 스레드를 대기시킬 수 있습니다.

의사 코드는 다음과 같습니다.

// Main thread starts
// Create CountDownLatch for N threads
// Create and start N threads
// Main thread waits on latch
// N threads completes there tasks are returns
// Main thread resume execution


답변

이와 같은 것을 사용하는 좋은 예는 Java Simple Serial Connector를 사용하여 직렬 포트에 액세스하는 것입니다. 일반적으로 포트에 무언가를 쓰고 다른 스레드에서는 비동기 적으로 장치가 SerialPortEventListener에서 응답합니다. 일반적으로 포트에 쓴 후 응답을 기다리기 위해 일시 ​​중지하려고합니다. 이 시나리오에서 스레드 잠금을 수동으로 처리하는 것은 매우 까다 롭지 만 Countdownlatch를 사용하는 것은 쉽습니다. 당신이 다른 방법으로 할 수 있다고 생각하기 전에, 당신이 결코 생각하지 않은 경쟁 조건에주의하십시오!

의사 코드 :

CountDownLatch latch;
void writeData() {
   latch = new CountDownLatch(1);
   serialPort.writeBytes(sb.toString().getBytes())
   try {
      latch.await(4, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
   }
}
class SerialPortReader implements SerialPortEventListener {
    public void serialEvent(SerialPortEvent event) {
        if(event.isRXCHAR()){//If data is available
            byte buffer[] = serialPort.readBytes(event.getEventValue());
            latch.countDown();
         }
     }
}


답변

atch.countDown () 호출 후 디버그를 추가하면 동작을 더 잘 이해하는 데 도움이 될 수 있습니다.

latch.countDown();
System.out.println("DONE "+this.latch); // Add this debug

출력에 카운트가 감소한 것으로 표시됩니다. 이 ‘count’는 실제로 countDown ()이 호출 되지 않았기 때문에 시작한 Runnable 태스크 (프로세서 오브젝트)의 수 이며, 이에 따라 atch.await ()에 대한 호출에서 기본 스레드가 차단됩니다.

DONE java.util.concurrent.CountDownLatch@70e69696[Count = 2]
DONE java.util.concurrent.CountDownLatch@70e69696[Count = 1]
DONE java.util.concurrent.CountDownLatch@70e69696[Count = 0]