2019년 12월 5일 목요일

[java] CountDownLatch와 CyclicBarrier의 차이 (Phaser로 들어가기 전)

출처 : https://www.baeldung.com/java-cyclicbarrier-countdownlatch

개요

사실 오래 전에 배워서 사용했던 Phaser라는 기능이 가물가물해서 기억을 하고자 CountDownLatch와 CyclicBarrier를 비교하면서 복습을 해보았다.
하지만 대부분 Phaser보다는 CountDownLath,CyclicBarrier를 많이 사용하지 않을까 싶다.


CountDownLatch

일단 Latch(빗장이었나?)로 걸어잠그고 숫자를 다 세면 열어주는 것이다. 그래서 만약에 CountDownLatch에 카운트를 5로 지정하면 0이 될때까지 현재 스레드가 기다린다.

CyclicBarrier

CyclicBarrier는 스레드 그룹이 모두 wait하고 있는 지점에 도달 할 때까지 기다린다.
이 시점에서 Barrier가 깨지면서 일을 할 수 있게 된다. 뭐 일을 안해도 되고 해도 된다.

차이점

Task vs Treads

보면 알겠지만 CountDownLatch는 숫자를 세는데 중점을 두고, CyclicBarrier는 모두 다 오길 기다린다.
CountDownLatch는 동일한 녀석이 숫자를 두번 세도 상관이 없다.
CyclicBarrier는 한명당 하나의 카운트라고 생각하면 된다. 무조건 다와야 한다.

CountDownLatch countDownLatch = new CountDownLatch(2);
Thread t = new Thread(() -> {
  countDownLatch.countDown();
  countDownLatch.countDown();
});

t.start();
countDownLatch.await();

System.out.println("code1");
System.out.println(0 == countDownLatch.getCount());
System.out.println("===");

CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
Thread t = new Thread(() -> {
  try {
    cyclicBarrier.await();
    cyclicBarrier.await();
  catch (InterruptedException | BrokenBarrierException e) {
    e.printStackTrace();
  }
});

t.start();

System.out.println("code2");
System.out.println(1 ==  cyclicBarrier.getNumberWaiting());
System.out.println(cyclicBarrier.isBroken());
System.out.println("===");


재사용성

CyclicBarrier는 wait()에 도달해서 다음으로 넘어가면 count값이 원래 값으로 바뀐다. 무한이 돈다
CountDownLatch에서는 한번 도달하면 끝이다.

List outputScraper = Collections.synchronizedList(new ArrayList<>());
CountDownLatch countDownLatch = new CountDownLatch(7);
ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
  es.execute(() -> {
    long prevValue = countDownLatch.getCount();
    countDownLatch.countDown();
    if (countDownLatch.getCount() != prevValue) {
      outputScraper.add("Count Updated");
    }
  });
}

es.shutdown();
System.out.println(outputScraper);
System.out.println(outputScraper.size() <= 7);
// https://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html
// https://stackoverflow.com/questions/6916385/is-there-a-concurrent-list-in-javas-jdk 참고
// Collections.synchronizedList는 내가 임의로 넣은 것이다.
List outputScraper = Collections.synchronizedList(new ArrayList<>());
CyclicBarrier cyclicBarrier = new CyclicBarrier(7);
ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
  es.execute(() -> {
    int numberWaiting = cyclicBarrier.getNumberWaiting();
    if (numberWaiting >= 0) {
      outputScraper.add("Count Updated"); 
    }
    try {
      cyclicBarrier.await();
    } catch (InterruptedException | BrokenBarrierException e) {
      e.printStackTrace();
    }
  });   
}

es.shutdown();
System.out.println(outputScraper);
System.out.println(outputScraper.size() > 7);
cyclicBarrier.await();
다음에는 Phaser에 대해 알아보자.