자바로 다중 작업(Multi Task)을 효과적으로 처리하는 방법들

자바로 다중 작업(Multi Task)을 효과적으로 처리하는 방법을 살펴보자. 자바에서 다중 작업을 처리하는데 기본으로 Thread 클래스와 Runnable 인터페이스를 사용한다. 하지만 이 Thread나 Runnable의 경우에는 병행처리를 하는 데 필요한 태스크 관리와 태스크의 분리(Divide)와 처리를 직접 구현해야 하는 단점이 있다. 그래서 자바 java.util.concurrent 패키지에는 다중 작업을 효과적으로 처리할 방법을 여러 가지 제공한다.

1. Executor 프레임웍

이 프레임웍은 처리해야 할 작업을 프로듀서-컨슈머 패턴의 큐에 저장하고, N개의 스레드를 사용해서 병행으로 처리하는 프레임웍이다. 일반적으로 프로듀스는 큐에 작업을 추가하고, 스레드 풀(컨슈머 스레드)에서 작업을 처리한다.

2. 카운트다운래치(CountDownLatch) & 사이클릭배리어(CyclicBarrier)

카운트다운래치(CountDownLatch)와 사이클릭배리어(CyclicBarrier)는 둘 다 큰 작업을 작은 작업으로 분리(개별 구현)하고, 이 분리한 작업의 실행을 완료한 뒤에 흐름을 진행하도록 흐름을 동기화시키는 클래스이다.
그림에서 보듯이, 카운트다운래치는 메인 스레드에서 작업을 처리하는 실행하는 스레드를 만들고 실행한다. 그리고 개별 스레드가 카운트다운래치의 값을 하나씩 제거하면, 메인 스레드가 시그널을 받아서 흐름을 진행하는 형태이다.

사이클릭배리어는 두 스레드 사이에 로직을 동기화하는 데 사용한다. 그래서 배리어를 사용해서 N개의 스레드 실행 흐름을 중단하다가, 배리어액션(단계를 지날 수 있는 과정)을 완료하면 N개의 스레드 실행 결과를 병합할 수 있는 형태이다. 그리고, 두 개의 스레드가 데이터를 교환하는데 사용하는 Exchanger는 사이클릭배리어의 특정 예이다.

3. ForkJoin 프레임웍

포크조인(ForkJoin) 프레임웍은 작업을 분리(분리하기 쉽게 선언)하고, 실행하고, 그리고 결과를 병합하기 쉽게 도와준다. 포크(Fork)는 작업을 분리하고, 조인(Join)은 분리한 작업이 완료한 뒤에 결과를 병합하는 과정이다. 이 프레임웍에 대한 자세한 내용은 http://www.oracle.com/technetwork/articles/java/fork-join-422606.html 에서 살펴보자.

이 그림에서 보듯이, 작업은 내부에서 fork()로 작업을 분리하고, join()으로 태스크를 병합한다. 그리고 이 작업은 재귀(Recursive)로 작업을 분리하는 형태라서 RecursiveTask 클래스를 상속해서 구현한다.

이제 어느 상황에서 이런 다중 작업을 처리하는 것이 좋은지 살펴보자.

1. 작업이 분리되지 않는 경우 “Executor”를 사용하자.

– 가장 많이 사용하는 형태이다.

2. 작업이 분리되는 경우에는 “카운트다운래치” 또는 “포크조인”을 사용하자.

– 많은 양의 데이터 정렬, 최대, 최소값 찾는 경우.
– 많은 양의 데이터를 합산하는 경우.
– 네트웍을 스캔하는 경우.
– 기타
개인적으로는 카운트다운래치를 많이 사용했지만, 작업 분리는 동일하지만 스레드 관리가 불편하다. 그래서 포크조인을 사용하는 것이 좋겠다.

3. 작업이 분리될 수도 있고 아닐 수도 있는 단계가 있는 작업은 “사이클릭배리어”를 사용하자.

– 예를 들어서 게임의 단계를 배리어도 두고 여러 스레드의 작업을 제어할 수 있다. 1단계를 지나면서, 랭킹정보, 개인정보, 게임정보등등을 가져오는 스레드를 실행하고, 필요한 정보를 다 가져온 뒤에 화면을 넘기는 예로 사용할 수 있겠다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.