'java.util.concurrent'에 해당되는 글 2건

  1. 멀티쓰레드 프로그램 평가기준 2010/06/01
  2. Callable을 사용하여 Runnable로부터 결과 반환 2008/05/28
유명한 Doug Lea 교수님의 멀티 쓰레드 프로그램의 평가기준에 대한 내용입니다.
"Java 언어로 배우는 디자인 패턴 입문 멀티 쓰레드편, Yuki Hiroshi" 의 Introduction 2 챕터에 나오는 내용입니다. 아래 내용의 출처는 http://ethdemor.springnote.com/pages/4349969 입니다.

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

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

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

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




'Java' 카테고리의 다른 글

외부 API를 JDK에서 내포한 경우, 최신의 외부 API 사용하기..  (0) 2010/06/22
#ifdef #ifndef in Java  (0) 2010/06/15
멀티쓰레드 프로그램 평가기준  (0) 2010/06/01
간단한 SOAP 메시지 콜  (0) 2010/05/26
garbage collector  (0) 2010/04/29

Java 1.4 부터 도입된 Doug Lea 교수의 java.util.concurrent 패키지에 Callable과 추가된 클래스를 통해서 쓰레드 풀링등의 기능을 쉽게 도입할 수가 있지요.. ^^

아래내용은
http://www.sdnkorea.com/blog/509 내용입니다.

저자 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;
  }

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

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

메서드 정의에서 보여 주듯이 Callable 개체를 ExecutorService에 제공하면 Future 개체가 반환됩니다. 그런 다음 Futureget() 메서드는 작업이 완료될 때까지 차단을 수행합니다. 이것은 첫 번째 예에서 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()은 호출 종료 시 반환된 값만을 필요로 합니다. Futureget() 메서드가 나중에 호출될 경우 이 예에서처럼 작업이 금방 실행되면 Future에서 값을 바로 받고, 작업이 오래 걸릴 경우에는 완료될 때까지 기다립니다. get()을 여러 번 호출한다고 해서 스레드에서 작업이 다시 실행되지는 않습니다.

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

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

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