개발 관련/Java

[JAVA:병렬 프로그래밍 - 1] CountDownLatch 사용하기.

snoworca 2014. 8. 19. 13:41

  만약 백그라운드에서 실행되고 있는 싱글 쓰레드가 종료되기를 기다리는 코드를 작성해야 한다면 당장 Thread 클래스의 join() 메소드 부터 떠올리게 될 것이다. 아래와 같이 말이다.


간단한 join() 사용예:

Thread thread = new Thread() {
	@Override
	public void run() {
		System.out.println("start trhead.");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("end trhead.");
	}
};
thread.start();
try {
	// 스레드가 끝날때 까지 대기한다.
	thread.join();
} catch (InterruptedException e) {
	e.printStackTrace();
}
System.out.println("Thread terminated.");



  하지만, 멀티 쓰레드에서 모든 동작이 끝나기를 기다려야 하는 상황이라면 join 을 사용하기 조금 난감하다. 이럴 땐 CountDownLatch 를 사용하면 참 편리하다. (안드로이드 API 설명 페이지로 링크합니다. 이 곳이 오라클 페이지보다 보기 좋아서;; http://developer.android.com/reference/java/util/concurrent/CountDownLatch.html )

   CountDownLatch 는 생성할 때 1 이상의 카운트를 인자값으로 받는다. 그리고 특정 메소드 내부에서 await() 메소드로 대기 상태를 만들어주고  CountDownLatch 를 생성할 때 설정한 count 가 0이 되기만들 기다렸다가 0이 되는 순간 대기 상태를 해제한다. 원리는 아래 그림과 같다.



  




   예제코드를 살펴보면 아래와 같다. 


CountDownLatch 를 활용한 예제코드:

public class main { private final static int THREADS = 10; private static CountDownLatch lacth = new CountDownLatch(THREADS); public static class RandomSleepRunnable implements Runnable { private int id = 0; private static Random random = new Random(System.currentTimeMillis()); public RandomSleepRunnable(int id) { this.id = id; } @Override public void run() { System.out.println("Thread(" + id + ") : Start."); // 1000ms 에서 2000ms 사이의 딜레이 값을 랜덤하게 생성. int delay = random.nextInt(1001) + 1000; try { System.out.println("Thread(" + id + ") : Sleep " + delay + "ms"); Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread(" + id + ") : End."); // lacth 의 카운터에서 -1. lacth.countDown(); } } public static void main(String[] args) { // 쓰레드를 10개 생성. for(int i = 0; i < THREADS; ++i) { new Thread(new RandomSleepRunnable(i)).start(); } try { // lacth 의 카운트가 0이 될 때 까지 대기한다. lacth.await(); // 아래와 같이 TimeOut 을 설정할 수 있다. // 아래의 경우는, 만약 2000ms 동안 latch 의 카운트가 0 되지 않는다면 // wait 상태를 해제하고 다음 동작으로 넘어간다. //lacth.await(2000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("All threads terminated."); } }



결과:

Thread(1) : Start.
Thread(1) : Sleep 1267ms
Thread(2) : Start.
Thread(0) : Start.
Thread(0) : Sleep 1168ms
Thread(2) : Sleep 1085ms
Thread(5) : Start.
Thread(4) : Start.
Thread(4) : Sleep 1573ms
Thread(5) : Sleep 1868ms
Thread(3) : Start.
Thread(3) : Sleep 1552ms
Thread(6) : Start.
Thread(6) : Sleep 1869ms
Thread(7) : Start.
Thread(8) : Start.
Thread(7) : Sleep 1001ms
Thread(8) : Sleep 1636ms
Thread(9) : Start.
Thread(9) : Sleep 1573ms
Thread(7) : End.
Thread(2) : End.
Thread(0) : End.
Thread(1) : End.
Thread(3) : End.
Thread(4) : End.
Thread(9) : End.
Thread(8) : End.
Thread(5) : End.
Thread(6) : End.
All threads terminated.

 


 매우 간단하고 사용하기도 편리하다.