ABOUT ME

기록을 전송

Today
Yesterday
Total
  • [java] CompletableFuture 문법
    JAVA 2024. 1. 31. 22:14

     

    비동기 프로그래밍

    업무중에 @Async 애노테이션이 붙은 메서드 안에 있는 Exception이 동작하지 않은 일이 있었다.

    정상화하기 위해 구글링을 하는데, 기본 개념이 부족해서 자료 읽기에 애를 먹었다.

     

    기본 문법을 정리해본다. (문법은 백기선 님의 JAVA 8 강의 내용을 요약했습니다.)

     

    우선 결론부터

     

    JAVA는 스레드를 관리하는 Executor라는 것을 제공.

    Executor를 이용해 스레드 생성, 작업, 끼어들기, 기다리기, 종료 같은 작업 가능.

    Future는 스레드 작업을 결과로 return한 값,

    Future api로 스레드에서 아직 작업중인지 확인, 작업이 끝나서 나온 결과 호출, 멀티스레드 결과 관리 가능

    CompleteableFuture는 자바 8에 등장한 Future 확장 기능

    Callback 함수 지원, Future를 조합, 예외처리 API를 지원

     

     

    • 자바 concurrent 프로그래밍
      • 멀티프로세싱 (ProcessBuilder - 프로세스 안에서 다른 프로세스 생성)
      • 멀티스레드
        • Thread
        • Runnable
    • 스레드를 만드는 방법
      • 스레드를 상속받아서 구현
      static class MyThread extends Thread {
      
      	@Override
      	public void run() {
      		// code
      	} 
      }
      
      • Runnable로 구현
      Thread thread = new Thread(new Runnable() {
      
      	@Override
      	public void run() {
      		// code
      	} 
      })
      
      thread.start();
      
      // 무한루프에 있다고 가정
      try {
      	thread.sleep(1000L); // 대기 시간중에 다른 스레드 있으면 그 스레드 일함
      } catch(InterruptedException e) {
      	// 누군가 끼어들면 종료
      	return;
      }
      
      thread.sleep(3000L);
      thread.interrupt(); // 다른 쓰레드 사이 끼어들기
      thread.join(); // 다른 쓰레드가 끝날때까지 기다리기
      
      • 람다로 구현
      Thread thread = new Thread(() -> { 
      	// code
      })
      
      thread.start();
      

    Executors

    • 위의 스레드를 만들고 조작하는 작업을 애플리케이션에서 분리하고 Executors에게 위임
    • 스레드 생성
    • 스레드 관리 (생명주기 관리)
    • 스레드 작업 처리 및 실행 API 제공

    주요 인터페이스

    • Executor: execute(Runnable)
    • ExecutorService - 스레드 관련 api 제공
    • ScheduledExecutorService - 반복작업 api 제공
    /* 스레드 셍성 */
    ExecutorService es = Executors.newSingleThreadExecutor(); // 싱글 스레드
    ExecutorService es = Executors.newFixedThreadPool(2); // 멀티 스레드 n 개 스레드 pool 생성
    
    /* 스레드 작업 실행 */
    // 1
    es.execute(new Runnable() {
    	@Override
    	public void run() {
    		// code
    	}
    });
    
    // 2
    es.submit(() -> { // code });
    
    /* 스레드 종료 */
    es.shutdown(); // graceful shutdown : 현재 진행중인 작업은 끝까지 마치고 죽는 것
    es.shutdownNow(); // 강제 종료 
    

     

    • Runnable은 void 이다.
    • 별도 스레드에서 작업한 결과를 return 하고 싶으면 Callable(java 1.5) 사용하면 된다.
      • 이때 return 하는 것이 future이다.

    Callable

    • 스레드 일 시킬때 Callable로 전달하면 작업 결과 값을 Future로 return할 수 있다.
    • Future로 여러작업 할 수 있음
    Callable<String> hello = () -> { 
    	Thread.sleap(2000L);
    	return "hello"; 
    }
    Future<String> helloFuture = es.submit(hello);
    
    helloFuture.get(); // blocking call - 2초 기다림
    helloFuture.isDone(); // future가 작업 진행중이면 fasle, 끝나면 true
    helloFuture.cancel(true); // 진행중 작업 멈추고 취소 
    helloFuture.cancel(false); // 기다렸다가 취소
    
    // 다중 callable - 모든 callable 끝날떼까지 기다린다. 
    // ex - 모든 주가 가져와서... 자산 총합 낸다.. 모두 기다렸다가 작업하는 경우 사용
    List<Future<String>> futures = es.invokeAll(Arrays.asList(one, two, three));
    for (Future<String> f : futures) {
    	System.out.println(f.get());
    }
    es.shutdown();
    
    // blocking call
    // 아무거나 빨리 끝난 작업 실행
    String s = es.invokeAny(Arrays.asList(one, two, three));
    System.out.println(s);
    es.shutdown();
    

    CompletableFuture

    • 자바 8에 등장, 비동기 프로그래밍 지원
    • Future의 문제점
      • 예외처리 API 없음
      • 여러 Future 조합이 어려움
      • future.get() 하기 전까지는 작업을 할 수 없다. - blocking call 이기때문
    • CompletableFuture (Impl Future, CompletableStage)
      • 외부에서 Complete를 명시적으로 할 수 있다.
    // 강제 작업 완료
    CompletableFuture<String> future = new CompletableFuture<>();
    future.complete("done");
    System.out.println(future.get()); // "done" 표시
    
    CompletableFuture<String> future = CompletableFuture.completedFuture("done");
    System.out.println(future.get()); // "done" 표시
    
    // 비동기 작업 실행 - 리턴값 없는 경우
    CompletableFuture.runAsync(() -> { return "Hello" }); // 리턴값 없는 경우
    System.out.println(future.get());
    
    // 비동기 작업 실행 - 리턴 있을 경우 콜백함수 실행
    CompletableFuture
    	.supplyAsync(() -> { return "Hello"; })
    	.thenApply((s) -> { return s.toUpperCase(); });
    System.out.println(future.get());
    
    // 비동기 작업 실행 - 리턴 없고 콜백함수 실행
    CompletableFuture
    	.supplyAsync(() -> { return "Hello"; })
    	.thenAccept((s) -> { s.toUpperCase(); });
    System.out.println(future.get());
    
    // 비동기 작업 실행 - 리턴 없고 콜백
    CompletableFuture
    	.supplyAsync(() -> { return "Hello"; })
    	.thenRun((s) -> { s.toUpperCase(); });
    System.out.println(future.get());
    

    CompletableFuture 조합, 예외처리

    • 조합
    // 서로 의존 있는 경우
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> { return "Hello"; });
    CompletableFuture<String> future2 = CompletableFuture.supplyAsync((message) -> { return message + "wolrd"; });
    
    CompletableFuture<String> resultFuture = future1.thenCompose(App::future2);
    resultFuture.get();
    
    // 서로 의존 없는 경우
    CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> { return "Hello"; });
    CompletableFuture<String> future4 = CompletableFuture.supplyAsync(() -> { return "wolrd"; });
    
    future3.thenCombine(future2, (f3, f4) -> f3 + " " + f4);
    
    // 한꺼번에 처리 - 
    CompletableFuture.allOf(f3, f4).then.... 
    
    • 에러처리
    ... .exceptionally(ex -> { ..code });
    ... .handle((result, ex) -> { ..분기처리.. });
    

     

    반응형

    'JAVA' 카테고리의 다른 글

    코딩 규칙 정하기 하루 전  (0) 2024.12.18
Designed by Tistory.