태그 보관물: java.util.concurrent

멀티스레드 프로그램 평가기준

유명하신 더그 리(Doug Lea) 교수님의 멀티 스레드 프로그램의 평가기준에 대한 내용이다. “Java 언어로 배우는 디자인 패턴 입문 멀티 스레드편, Yuki Hiroshi” 의 Introduction 2 챕터에 나오는 내용이다.

아래의 기준은 더그 리 분류를 기준으로 한 것이며, 이런 류의 평가 기준은 대개 상반(trade-off)되는 경우가 있다.

필 수
– 안전성 (safety) – 객체를 망가트리지 않을 것
– “망가 진다”는 것은 비유적 표현으로 객체의 필드가 예상 외의 값을 취하는 것을 의미합니다.
– 복수의 스레드가 이용해도 안전성이 유지되는 클래스를 스레드 세이프(thread-safe)한 클래스라고 하며 자바 API에는 명시되어 있습니다.
– 생존성 (liveness) – 필요한 처리가 이뤄질 것

옵 션
재사용성 (resuability) – 클래스를 다시 사용할 수 있는 것
– 멀티 스레드 프로그램에서는 스레드 배타제어, 구조, 방침을 캡슐화 하는 것이 관건이며 Java SE 5.0의 java.util.concurrent 패키지에서 이를 잘 구현하고 있다.
– 수행 능력(performance) – 고속 / 대량으로 처리할 수 있을 것
– throughput – 단위 시간 당 처리 수
– 응답성 (responsiveness) – 요청을 한 뒤 반응이 나타날 때까지 걸리는 시간
– GUI 프로그램에서는 요청의 완료까지 걸리는 시간보다 처리 시작까지의 시간을 중요하게 생각하기도 합니다. 여기서 완료는 요청 -> 처리 -> 응답 까지의 시간이며 처 리 시작은 완료까지의 시간은 좀 더 걸리더라도 클라이언트에게 먼저 응답(~을 처리 중입니다 등)을 보내는 것을 말합니다.
– 응답성이 높은 프로그램은 대기 시간(latency)이 짧다고 표현한다.
– 용량 (capacity) – 동시에 처리할 수 있는 수
– 그 밖에 효율(efficiency), 확장성(scalability), 저하(degradation) 등이 있다.

평가기준 정리
멀티 스레드 시스템에서 안전성과 생존성이 지켜지지 않으면 제대로된 프로그램이라고 할 수 없다. 따라서 이 필수 조건을 만족한 상태에서 재사용성과 수행 능력을 어떻게 높이느냐가 관건이 된다.

Callable을 사용해서 Runnable의 실행 결과 반환

자바 1.4에 추가된 더그 리(Doug Lea) 교수의 java.util.concurrent 패키지에 Callable과 그 외의 클래스를 사용해서 스레드 풀링 등의 기능을 쉽게 사용할 수가 있다. 아래는 Callable을 사용해서 스레드 실행 결과를 확인하는 내용이다.

그리고 이 내용은 sdnkorea의 내용이었는데, 없어져서 링크는 삭제했다.


저자 John Zukowski
Runnable 인터페이스는 자바 플랫폼 초기부터 사용되어 왔습니다. 이 인터페이스를 사용하면 완료할 작업을 스레드별로 정의할 수 있습니다. 대부분의 사용자들이 이미 알고 있듯이 이 인터페이스는 run() 이라는 단일 메서드를 사용하는데, 이 메서드는 인수를 허용하지 않고 값을 반환하지 않으며 확인된 어떤 예외도 반환할 수 없습니다. 방금 완료된 작업으로부터 값을 반환 받으려면 인터페이스 외부의 메서드를 사용하여 작업이 완료되었다는 일종의 알림 메시지를 기다려야 합니다. 예를 들어, 이러한 시나리오의 경우 다음과 같이 코딩해야 합니다.

 Runnable runnable = ...;
  Thread t = new Thread(runnable);
  t.start();
  t.join();
  String value = someMethodtoGetSavedValue()

원칙적으로 이 코드에 잘못된 점은 없지만 J2SE 5.0에서 소개된 Callable 인터페이스 덕분에 이제는 다른 방법을 사용할 수 있습니다. run() 메서드를 사용하는 대신 Callable 인터페이스는 call() 메서드를 제공하며 이 메서드는 Object를 반환하거나, 보다 구체적으로 generic화된 형식에 사용된 유형을 반환합니다.

public interface Callable<V> {
     V call() throws Exception;
  }

실행할 Thread로 Callable을 전달할 수 없기 때문에 대신 ExecutorService를 사용하여 Callable 개체를 실행합니다. 이 서비스는 Callable 개체를 수락하여 submit() 메서드를 통해 실행합니다.

<T> Future<T> submit(Callable<T> task)

메서드 정의에서 보여 주듯이 Callable 개체를 ExecutorService에 제공하면 Future 개체가 반환됩니다. 그런 다음 Future의 get() 메서드는 작업이 완료될 때까지 차단을 수행합니다. 이것은 첫 번째 예에서 join() 호출과 동일합니다. 실제로 이 메서드는 join() 호출과 동일하며, get()이 Callable 인스턴스에서 계산한 값을 반환할 때 get value 호출과도 동일합니다.

이를 설명하기 위해 다음 예에서는 명령줄에서 전달된 각 단어에 대해 Callable 인스턴스를 개별적으로 만들어 길이를 모두 합산합니다. 각각의 Callable은 개별 단어의 합만을 계산합니다. Future 개체 집합은 각각으로부터 계산된 값을 얻기 위해 저장됩니다. 반환된 값의 순서를 유지해야 할 경우 List를 대신 사용할 수 있습니다.

import java.util.*;
import java.util.concurrent.*;
public class CallableExample {

public static class WordLengthCallable
implements Callable {
private String word;
public WordLengthCallable(String word) {
this.word = word;
}
public Integer call() {
return Integer.valueOf(word.length());
}
}

public static void main(String args[]) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(3);
Set<Future<Integer>> set = new HashSet<Future&lg;Integer>>();
for (String word: args) {
Callable<Integer> callable = new WordLengthCallable(word);
Future<Integer> future = pool.submit(callable);
set.add(future);
}
int sum = 0;
for (Future<Integer> future : set) {
sum += future.get();
}
System.out.printf("The sum of lengths is %s%n", sum);
System.exit(sum);
}
}

WordLengthCallable은 각 단어를 저장하고 이 단어의 길이를 call() 메서드의 반환 값으로 사용합니다. 이 값은 생성하는 데 시간이 다소 걸릴 수 있지만 이 예에서는 즉시 생성됩니다. call()은 호출 종료 시 반환된 값만을 필요로 합니다. Future의 get() 메서드가 나중에 호출될 경우 이 예에서처럼 작업이 금방 실행되면 Future에서 값을 바로 받고, 작업이 오래 걸릴 경우에는 완료될 때까지 기다립니다. get()을 여러 번 호출한다고 해서 스레드에서 작업이 다시 실행되지는 않습니다.

이 프로그램의 목표는 단어 길이의 합을 계산하는 것이므로 Callable 작업의 완료 순서는 문제가 되지 않습니다. 처음 세 개의 작업이 완료되기 전에 마지막 작업이 완료되어도 상관 없습니다. Future에 대한 첫 번째 get() 호출은 Set의 첫 번째 작업이 완료되기만을 기다립니다. 이는 다른 작업이 개별적으로 실행되는 것을 막지 않으며, 단지 하나의 스레드 또는 작업이 완료되기를 기다립니다.

제공되는 모든 서비스는 고정 크기의 스레드 풀을 사용할 것이므로 이 특정 예에서는 ExecutorService에 대해 고정 크기의 스레드 풀을 사용합니다.

실행자 및 스레드 풀 사용에 대한 자세한 내용은 Java Tutorial의 Executors 단원을 참조하십시오. SwingWorker 클래스는 약간 다르긴 하지만 Future와 함께 작동하는 Runnable 객체의 또 다른 예입니다. 이에 대한 자세한 내용은 Worker Threads and SwingWorker 단원을 참조하십시오.