🌏 WEB/JAVA

λ‚΄κ°€ 보렀고 μ •λ¦¬ν•˜λŠ” JAVA λ©€ν‹° μŠ€λ ˆλ“œ 5- μŠ€λ ˆλ“œν’€

μ• μ •μ“° 2021. 10. 6. 18:27

μŠ€λ ˆλ“œν’€μ€ μž‘μ—… μ²˜λ¦¬μ— μ‚¬μš©λ˜λŠ” μŠ€λ ˆλ“œλ₯Ό μ œν•œλœ 개수만큼 μ •ν•΄ 놓고 μž‘μ—… 큐에 λ“€μ–΄μ˜€λŠ” μž‘μ—…λ“€μ„ ν•˜λ‚˜μ”© μŠ€λ ˆλ“œκ°€ 맑아 μ²˜λ¦¬ν•œλ‹€.

병렬 μž‘μ—… μ²˜λ¦¬κ°€ λ§Žμ•„μ§€λ©΄ μŠ€λ ˆλ“œ κ°œμˆ˜κ°€ μ¦κ°€λ˜λŠ”λ° 그에 λ”°λ₯Έ μŠ€λ ˆλ“œ 생성, μŠ€μΌ€μ€„λ§μœΌλ‘œ 인해 CPUκ°€ λ°”λΉ μ Έ λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ λŠ˜μ–΄λ‚œλ‹€.

λ”°λΌμ„œ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μ„±λŠ₯이 μ €ν™” λ˜λ―€λ‘œ μŠ€λ ˆλ“œ 풀을 μ‚¬μš©ν•˜μ—¬ μŠ€λ ˆλ“œ 전체 κ°œμˆ˜κ°€ λŠ˜μ–΄λ‚˜μ§€ μ•Šλ„λ‘ ν•΄μ•Όν•œλ‹€.

 

μŠ€λ ˆλ“œν’€ 생성

public class ExecutorExample {
  public static void main(String[] args) {
      // 1개의 μŠ€λ ˆλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” μŠ€λ ˆλ“œν’€ 생성
      ExecutorService singleThread = Executors.newSingleThreadExecutor();
      // μŠ€λ ˆλ“œλ₯Ό μ œν•œ 없이 μ‚¬μš©ν•˜λŠ” μŠ€λ ˆλ“œν’€ 생성
      ExecutorService cachedThread = Executors.newCachedThreadPool();
      // 3개의 μŠ€λ ˆλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” μŠ€λ ˆλ“œν’€ 생성
      ExecutorService fixedThread = Executors.newFixedThreadPool(3);
      // CPU μ½”μ–΄μ˜ 수만큼 μ΅œλŒ€ μŠ€λ ˆλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” μŠ€λ ˆλ“œν’€ 생성
      ExecutorService maxFixedThread = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  }
}

μŠ€λ ˆλ“œν’€ μ’…λ£Œ

μŠ€λ ˆλ“œν’€μ˜ μŠ€λ ˆλ“œλŠ” 기본적으둜 데λͺ¬ μŠ€λ ˆλ“œκ°€ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ— mian μŠ€λ ˆλ“œκ°€ μ’…λ£Œλ˜μ–΄λ„ μž‘μ—…μ„ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ 계속 μ‹€ν–‰ μƒνƒœλ‘œ λ‚¨μ•„μžˆλ‹€.

// λ‚¨μ•„μžˆλŠ” μž‘μ—…μ„ λ§ˆλ¬΄λ¦¬ν•˜κ³  μ’…λ£Œν•œλ‹€.
executorService.shutdown();
// λ‚¨μ•„μžˆλŠ” μž‘μ—…κ³Ό 상관없이 κ°•μ œλ‘œ μ’…λ£Œν•œλ‹€.
executorService.shutdownNow();

μŠ€λ ˆλ“œ ν’€ μž‘μ—… 생성

ν•˜λ‚˜μ˜ μž‘μ—…μ€ Runnable , Callable κ΅¬ν˜„ 클래슀둜 ν‘œν˜„ν•œλ‹€. λ‘˜μ˜ 차이점은

Runnable -> 리턴값이 μ‘΄μž¬ν•˜μ§€ μ•ŠμŒ.

Runnable task = new Runnable(){
	@Override
    public void run(){
    	// μž‘μ—… λ‚΄μš©
	}
}

Callable -> 리턴값이 μ‘΄μž¬ν•¨

Callable<T> task = new Callable<T>(){
	@Override
    public T call() throws Exceoption{
    	//μž‘μ—… λ‚΄μš©
	return T;
	}
}

 

μž‘μ—… 처리 μš”μ²­

ExecutorService의 μž‘μ—… 큐에 Runnable λ˜λŠ” Callable 객체λ₯Ό λ„£λŠ” ν–‰μœ„λ‹€.

execute()λŠ” μž‘μ—… 처리 κ²°κ³Όλ₯Ό 받지 λͺ»ν•˜κ³  μž‘μ—… 처리 도쀑 μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ©΄ μŠ€λ ˆλ“œκ°€ μ’…λ£Œλœλ‹€.

submit() 은 μž‘μ—… 처리 κ²°κ³Όλ₯Ό 받을 수 μžˆλ„λ‘ Futureλ₯Ό λ¦¬ν„΄ν•˜κ³  μž‘μ—… 처리 도쀑 μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ”λΌλ„ μŠ€λ ˆλ“œλŠ” μ’…λ£Œλ˜μ§€ μ•Šκ³  λ‹€μŒ μž‘μ—…μ„ μœ„ν•΄ μž¬μ‚¬μš© ν•œλ‹€. 

 

-> 가급적 생성 μ˜€λ²„ν—€λ”λ₯Ό 쀄이기 μœ„ν•΄ submit()을 μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€.

 

execute() λ₯Ό μ‚¬μš©ν–ˆμ„ λ•Œ

μŠ€λ ˆλ“œμ˜ κ°œμˆ˜λŠ” 변함이 μ—†κ³  μ‹€ν–‰ μŠ€λ ˆλ“œμ˜ 이름이 λͺ¨λ‘ λ‹€λ₯Έκ²ƒμ„ 확인할 수 μžˆλ‹€.

이것은 μž‘μ—… 처리 도쀑 μ˜ˆμ™Έκ°€ λ°œμƒν–ˆκΈ° λ•Œλ¬Έμ— ν•΄λ‹Ή μŠ€λ ˆλ“œλŠ” 제거되고 μƒˆ μŠ€λ ˆλ“œκ°€ 계속 μƒμ„±λ˜κΈ° λ•Œλ¬Έμ΄λ‹€.

      for (int i=0;i<10;i++){
          Runnable runnable = new Runnable() {
              @Override
              public void run() {
                  ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) fixedThread;
                  int poolSize = threadPoolExecutor.getPoolSize();
                  String threadName = Thread.currentThread().getName();
                  System.out.println("총 μŠ€λ ˆλ“œ 개수 : "+poolSize+"μž‘μ—… μŠ€λ ˆλ“œ 이름 :" + threadName);

                  //μ˜ˆμ™Έ λ°œμƒ μ‹œν‚΄
                  int value = Integer.parseInt("μ‚Ό");
              }
          };
          // μž‘μ—… 처리 μš”μ²­
          fixedThread.execute(runnable);

 

submit() 을 μ‚¬μš©ν–ˆμ„ λ•Œ

μ•„λž˜μ™€ 같이 μŠ€λ ˆλ“œ 이름이 λŠ˜μ–΄λ‚˜μ§€ μ•ŠλŠ”κ²ƒμ„ 보면  μŠ€λ ˆλ“œκ°€ μ’…λ£Œλ˜μ§€ μ•Šκ³  μž¬μ‚¬μš© λ˜λŠ” 것을 λ³Ό 수 μžˆλ‹€.

 

 

λΈ”λ‘œν‚Ή λ°©μ‹μ˜ μž‘μ—… μ™„λ£Œ 톡보

submit() λ©”μ„œλ“œλŠ” λ§€κ°œκ°’μœΌλ‘œ μ€€ Runnable μ΄λ‚˜ Callable μž‘μ—…μ„ μŠ€λ ˆλ“œ ν’€μ˜ μž‘μ—… 큐에 μ €μž₯ν•˜κ³  μ¦‰μ‹œ Future객체λ₯Ό λ¦¬ν„΄ν•œλ‹€.

Future κ°μ±„λŠ” μž‘μ—… κ²°κ³Όκ°€ μ•„λ‹ˆλΌ μž‘μ—…μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ κΈ°λ‹€λ Έλ‹€κ°€(λΈ”λ‘œν‚Ή) μ΅œμ’… κ²°κ³Όλ₯Ό μ–»λŠ”λ° μ‚¬μš©λœλ‹€. (지연 μ™„λ£Œ 객채)

 

Futrue의 get() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ μž‘μ—…μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ λΈ”λ‘œν‚Ήλ˜μ—ˆλ‹€κ°€ 처리결과 Vλ₯Ό λ¦¬ν„΄ν•œλ‹€.(Future<V>)

get(long timeout,TimeUnit unit) 을 μ‚¬μš©ν•˜λ©΄ μ‹œκ°„ 전에 μž‘μ—…μ΄ μ™„λ£Œλ˜λ©΄ κ²°κ³Ό Vλ₯Ό λ¦¬ν„΄ν•˜μ§€λ§Œ μ™„λ£Œλ˜μ§€ μ•ŠμœΌλ©΄ Exception을 λ°œμƒμ‹œν‚¨λ‹€.

 

submit(Runnable task) future.get() -> null 리턴

submit(Runnable task, Integer result) future.get() -> int λ°˜ν™˜

submit(Callable<String> task) future.get() -> String λ°˜ν™˜

 

μœ„μ™€ 같이 λΈ”λ‘œν‚Ή λ°©μ‹μ˜ μž‘μ—… μ™„λ£Œ ν†΅λ³΄μ—μ„œ μ£Όμ˜ν•  점은 μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” μŠ€λ ˆλ“œκ°€ μž‘μ—…μ„ μ™„λ£Œν•˜κΈ° κΉŒμ§€λŠ”

get() λ©”μ„œλ“œκ°€ λΈ”λ‘œν‚Ή λ˜λ―€λ‘œ λ‹€λ₯Έ μ½”λ“œλ₯Ό μ‹€ν–‰ν•  수 μ—†λ‹€.

κ·Έλ ‡κΈ° λ•Œλ¬Έμ— μ•„λž˜μ™€ 같이 get() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” μŠ€λ ˆλ“œλŠ” μƒˆλ‘œμš΄ μŠ€λ ˆλ“œμ΄κ±°λ‚˜ μŠ€λ ˆλ“œν’€ μ•ˆμ— μžˆλŠ” λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ λ˜μ–΄μ•Ό ν•œλ‹€.

 

// μƒˆλ‘œμš΄ μŠ€λ ˆλ“œλ₯Ό μƒμ„±ν•΄μ„œ 호좜

new Thread (new Runnalbe(){
	@Overrid
	public void run(){
    	try{
		future.get();
            }catch (Exception e){
		e.printStackTrace();
            }
	}
 }
 }).start();
 
 // μŠ€λ ˆλ“œν’€μ˜ μŠ€λ ˆλ“œκ°€ 호좜
 
 executorService.submit(new Runnable(){
	@Override
	public void run(){
    	try{
		future.get();
            }catch (Excetion e){
		e.printStackTrace();
            }
	}
 });

 

Future κ°μ²΄λŠ” μž‘μ—… κ²°κ³Όλ₯Ό μ–»κΈ° μœ„ν•œ get() λ©”μ„œλ“œ 이외에도 

cancel : μž‘μ—…μ‹œμž‘μ „ λ§€κ°œκ°’κ³Ό 상관없이 μ·¨μ†Œ ν›„ true /μž‘μ—…μ΄ 진행 쀑일 경우 λ§€κ°œκ°’μ΄ true 일 κ²½μš°μ—λ§Œ μž‘μ—… μŠ€λ ˆλ“œλ₯Ό interrupt

isCancelled : μž‘μ—…μ΄ μ™„λ£Œλ˜μ—ˆκ±°λ‚˜ μ–΄λ–€ 이유둜 인해 μ·¨μ†Œν•  수 μ—†λ‹€λ©΄ false 

isDone : μž‘μ—…μ΄ 정상적,μ˜ˆμ™Έ,μ·¨μ†Œ λ“± μ™„λ£Œλ˜μ—ˆλ‹€λ©΄ true

 

1. 리턴값이 μ—†λŠ” μž‘μ—… μ™„λ£Œ 톡보

submit λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€.

결과값이 μ—†μŒμ—λ„ λΆˆκ΅¬ν•˜κ³  Future객체λ₯Ό λ¦¬ν„΄ν•˜λŠ”λ°, 이것은 μŠ€λ ˆλ“œκ°€ μž‘μ—… 처리λ₯Ό μ •μƒμ μœΌλ‘œ μ™„λ£Œν–ˆλŠ”μ§€ μ˜ˆμ™Έκ°€ λ°œμƒν–ˆλŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄μ„œμ΄λ‹€.

Future future = executorService.submit(task);


// null을 λ¦¬ν„΄ν•˜μ§€λ§Œ interruptλ˜κ±°λ‚˜ μ˜ˆμ™Έκ°€ λ°œμƒν•  경우λ₯Ό μœ„ν•΄ μ˜ˆμ™Έ 처리 μ½”λ“œκ°€ ν•„μš”ν•˜λ‹€.

try{
	future.get(); // null
	}catch(InterruptedException e){
    	// μž‘μ—… 처리 도쀑 μŠ€λ ˆλ“œκ°€ interrupt 될 경우 μ‹€ν–‰ν•  μ½”λ“œ
	}catch(ExecutionException e){
    	// μž‘μ—… 처리 도쀑 μ˜ˆμ™Έκ°€ λ°œμƒλœ 경우 μ‹€ν–‰ν•  μ½”λ“œ
    }

 

2. 리턴값이 μžˆλŠ” μž‘μ—… μ™„λ£Œ 톡보

μŠ€λ ˆλ“œκ°€ μž‘μ—… ν›„ 처리 κ²°κ³Όλ₯Ό μ–»μ–΄μ•Ό ν•œλ‹€λ©΄ Callable 객체λ₯Ό μƒμ„±ν•˜λ©΄ λœλ‹€.

// Callable 객체 생성
Callable<T> task = new Callable<T>() {
	@Override
	public T call() throws Exception{
    	//μŠ€λ ˆλ“œκ°€ μ²˜λ¦¬ν•  μž‘μ—… λ‚΄μš©
        return T;
	}
};

// submit λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄ Future<T>λ₯Ό 리턴
Future<T> future = executorService.submit(task);

// μ˜ˆμ™Έμ²˜λ¦¬
try{
	T result = future.get();
    }catch(InterruptedException e){
	// μž‘μ—… 처리 도쀑 μŠ€λ ˆλ“œκ°€ interrupt 될 경우
    }catch(ExecutionException e){
    // μž‘μ—… 처리 도쀑 μ˜ˆμ™Έ

 

3. μž‘μ—… 처리 κ²°κ³Όλ₯Ό μ™ΈλΆ€ 객체에 μ €μž₯

μ™ΈλΆ€ Result 객체에 μž‘μ—… κ²°κ³Όλ₯Ό μ €μž₯ν•˜μ—¬ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ 객체λ₯Ό μ‚¬μš©ν•΄ μ–΄λ–€ μž‘μ—…μ„ 진행할 수 있게 ν•΄μ€€λ‹€.

λŒ€κ²Œ Result κ°μ²΄λŠ” 곡유 객체가 λ˜μ–΄, 두 개 μ΄μƒμ˜ μŠ€λ ˆλ“œ μž‘μ—…μ„ μ·¨ν•©ν•  λͺ©μ μœΌλ‘œ μ‚¬μš©λœλ‹€.

 

μ•„λž˜μ™€ 같이 곡유 객체가 λ˜μ–΄ μ‚¬μš©λœλ‹€.

// 곡유 객체 생성
Result result = new Result();
// μž‘μ—… 객체 생성
Runnable task = new Task(result);
// μž‘μ—… 처리 μš”μ²­
Future<Result> future = excutorServie.submit(task,result);

// κ²°κ³Ό λ°›κΈ° or μ˜ˆμ™Έ 처리
try{
    result = future.get();
    }catch (Excetion e){
    e.printStackTrace();
    }


// 곡유 객체
class Result {
	int accumValue;
    synchronized void addValue(int value){
    accumValue += value;
    }
}

 

4. μž‘μ—… μ™„λ£Œ 순으둜 톡보

μž‘μ—…μ˜ μ–‘κ³Ό μŠ€λ ˆλ“œ μŠ€μΌ€μ€„λ§μ— λ”°λΌμ„œ λ¨Όμ € μš”μ²­ν•œ μž‘μ—…μ΄ λ‚˜μ€‘μ— μ™„λ£Œλ˜λŠ” κ²½μš°λ„ λ°œμƒν•œλ‹€.

순차적일 ν•„μš”κ°€ 없을 경우 μ²˜λ¦¬κ°€ μ™„λ£Œλœ 것뢀터 κ²°κ³Όλ₯Ό μ–»μ–΄ μ‚¬μš©ν•  수 μžˆλ‹€.

CompletionService 의 poll() take() λ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€.

 

poll() -> μ™„λ£Œλœ μž‘μ—…μ΄ μ—†λ‹€λ©΄ μ¦‰μ‹œ null return

take() -> μ™„λ£Œλœ μž‘μ—…μ΄ μ—†λ‹€λ©΄ μžˆμ„ λ•ŒκΉŒμ§€ λΈ”λ‘œν‚Ήλ¨

 

ExcutorService executorService = Excutors.newFixedThreadPool(
	Runtime.getRuntime().availableProcessors());

// 객체λ₯Ό 생성할 λ•Œ μƒμ„±μž λ§€κ°œκ°’μœΌλ‘œ ExecutorServiceλ₯Ό 제곡
CompletionService<V> completionService = new ExecutorCompletionService<V>{
	executorService);

// submit() λ©”μ„œλ“œλ‘œ μž‘μ—… 처리 μš”μ²­
// μŠ€λ ˆλ“œν’€μ—κ²Œ μž‘μ—… 처리 μš”μ²­
completionService.submit(Callable<T> task);
completionService.submit(Runable task, V result);


// while문이 μžˆμ„ 경우 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ’…λ£Œλ  λ•ŒκΉŒμ§€ 반볡 μ‹€ν–‰ν•΄μ•Ό ν•˜λ―€λ‘œ μŠ€λ ˆλ“œν’€μ˜ μŠ€λ ˆλ“œμ—μ„œ μ‹€ν–‰ν•˜λŠ” 것이 μ’‹λ‹€.
executorService.submit(new Runnable(){
	@Override
    public void run(){
    	while(true){
        	try{
            	// μ™„λ£Œλœ 직업 κ°€μ Έμ˜€κΈ°
            	Future<Integer> future = completionService.task();
                int value = future.get();
                } catch (Exciption e) {
                break;
                }

 

5. 콜백 λ°©μ‹μ˜ μž‘μ—… μ™„λ£Œ 톡보

μŠ€λ ˆλ“œκ°€ μž‘μ—…μ„ μ™„λ£Œν•˜λ©΄ νŠΉμ • λ©”μ„œλ“œλ₯Ό μžλ™ μ‹€ν–‰ν•˜λŠ” 기법이닀. μ΄λ•Œ μžλ™ μ‹€ν–‰λ˜λŠ” λ©”μ„œλ“œλ₯Ό 콜백 λ©”μ„œλ“œλΌκ³  ν•œλ‹€.

 

λΈ”λ‘œν‚Ή 방식과 콜백 λ°©μ‹μ˜ 차이점은 λ¬΄μ—‡μΌκΉŒ?

λΈ”λ‘œν‚Ή 방식은 μž‘μ—… 처리λ₯Ό μš”μ²­ν•œ ν›„ μž‘μ—…μ΄ μ™„λ£Œλ  λ•ŒκΉŒμ§€ λΈ”λ‘œν‚Ήλ˜μ§€λ§Œ

콜백 방식은 μž‘μ—… 처리λ₯Ό μš”μ²­ν•œ ν›„ κ²°κ³Όλ₯Ό 기닀릴 ν•„μš” 없이 λ‹€λ₯Έ κΈ°λŠ₯을 μˆ˜ν–‰ν•  수 μžˆλ‹€.  -> μž‘μ—… μ²˜λ¦¬κ°€ μ™„λ£Œλ˜λ©΄ μžλ™μœΌλ‘œ 콜백 λ©”μ„œλ“œκ°€ μ‹€ν–‰λ˜μ–΄ κ²°κ³Όλ₯Ό μ•Œ 수 있기 λ•Œλ¬Έ

 

ExecutorServicesms 은 μ½œλ°±μ„ μœ„ν•΄ λ³„λ„μ˜ κΈ°λŠ₯을 μ œκ³΅ν•˜μ§€ μ•Šμ§€λ§Œ Runnable κ΅¬ν˜„ 클래슀λ₯Ό μž‘μ„±ν•  λ•Œ 콜백 κΈ°λŠ₯을 κ΅¬ν˜„ν•  수 μžˆλ‹€.

직접 μ •μ˜ν•΄λ„ μ’‹κ³  CompletionHandlerλ₯Ό μ΄μš©ν•΄λ„ μ’‹λ‹€.

 

CompletionHandler<V,A> callback = new CompletionHandler<V,A>(){
	@Override
    public void completed(V result,A attachment){
    // μž‘μ—…μ„ 정상 처리 μ™„λ£Œν–ˆμ„ λ–„ ν˜ΈμΆœλ˜λŠ” 콜백 λ©”μ„œλ“œ
    }
    @Override
    public void failed(Throwable exc, A attachemnt){
    // μž‘μ—… 처리 도쀑 μ˜ˆμ™Έκ°€ λ°œμƒν–ˆμ„ λ–„ ν˜ΈμΆœλ˜λŠ” 콜백 λ©”μ„œλ“œ
    };
}

Runnable task = new Runnable(){
	@Override
    public void run(){
    	try{
		// μž‘μ—…μ²˜λ¦¬
		V result = ...;
		// μž‘μ—…μ„ 정상 μ²˜λ¦¬ν–ˆμ„ 경우 호좜
		callback.completed(result,null);
           }catch(Exception e){
		// μ˜ˆμ™Έκ°€ λ°œμƒ ν–ˆμ„ 경우 호좜
		callback.failed(e,null);
           }
 	}
};

μ—¬κΈ°μ„œ Vνƒ€μž… νŒŒλΌλ―Έν„°λŠ” κ²°κ³Όκ°’μ˜ νƒ€μž…μ΄κ³  AλŠ” 첨뢀값(콜백 λ©”μ„œλ“œμ— κ²°κ³Όκ°’ 이외 μΆ”κ°€μ μœΌλ‘œ 전달 ν•˜λŠ” 객체)의 νƒ€μž…μ΄λ‹€. 

λ§Œμ•½ 첨뢀값이 ν•„μš” μ—†λ‹€λ©΄ Void둜 지정해주면 λœλ‹€.

 

(예제)

import javax.annotation.processing.Completion;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CallbackExample {

    // μŠ€λ ˆλ“œν’€ μ„ μ–Έ
    private ExecutorService executorService;

    // μŠ€λ ˆλ“œ 풀에 μŠ€λ ˆλ“œ 갯수 μ΄ˆκΈ°ν™”
    public CallbackExample(){
        executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    }

    // V - Integer A - Void 으둜 μ½œλ°±λ©”μ„œλ“œλ₯Ό 가진 객체 생성
    private CompletionHandler<Integer,Void> callback =
            new CompletionHandler<Integer, Void>() {
                @Override
                public void completed(Integer result, Void attachment) {
                    // μž‘μ—…μ΄ 정상 처리 λ˜μ—ˆμ„ λ•Œ 호좜
                    System.out.println("completed() μ‹€ν–‰ : " +result);
                }

                @Override
                public void failed(Throwable exc, Void attachment) {
                    // μ˜ˆμ™Έκ°€ λ°œμƒ ν–ˆμ„ 경우 호좜
                    System.out.println("failed() μ‹€ν–‰ : " + exc.toString());
                }
            };

    public void doWork(final String x, final String y){
        Runnable task = new Runnable() {
            @Override
            public void run() {
                try{
                    int intX = Integer.parseInt(x);
                    int intY = Integer.parseInt(y);
                    int result = intX+intY;
                    // μ •μƒμ²˜λ¦¬ μ½œλ°±ν•¨μˆ˜ 호좜
                    callback.completed(result,null);
                }catch (NumberFormatException e){
                    // μ˜ˆμ™Έ μ½œλ°±ν•¨μˆ˜ 호좜
                    callback.failed(e,null);
                }
            }
        };
        // μŠ€λ ˆλ“œν’€μ—κ²Œ μž‘μ—… μš”μ²­
        executorService.submit(task);

    }
    public void finish(){
        executorService.shutdown();
    }


  public static void main(String[] args) {
    CallbackExample example = new CallbackExample();
    example.doWork("3","3");
    // μ—λŸ¬ λ°œμƒ
    example.doWork("3","μ‚Ό");
    example.finish();
  }
}

λ°˜μ‘ν˜•