태그 보관물: Concurrent

Concurrent vs Parallel

Concurrent와 Parallel의 개념에 대한 내용입니다..
전에 http://choiwonwoo2.tistory.com/ 님께서 concurrent와 parallel의 차이에 대해서 설명해 주신 내용이 알듯말듯.. 했는데, 아래의 링크의 내용을 보니 명확하게 눈에 들어오네요.. ^^

http://www.google.com/buzz/leedaeyeop/2NL6hyr45hL/Parallel-programming-in-NET-Introduction-http

위 링크의 내용은 다음과 같다.

Concurrent applications tend to create a thread that
handles a whole series of tasks. Most of the time these concurrent
applications create threads because they need an isolated process for a
concurrent event.
Parallel applications divide a process into small tasks
that are executed on seperate threads. Because the tasks are small, the
threads can be divided evenly over the processors, resulting in very
efficient use of a multi-core CPU.

아래의 이미지는 concurrent와 parallel의 차이에 대한 좋은 예입니다.

 

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 단원을 참조하십시오.