모던 자바 인 액션 15장 : CompletableFuture와 리액티브 프로그래밍 컨셉의 기초

Adam·2024년 7월 8일
0

모던 자바 인 액션

목록 보기
15/20

동시성을 구현하는 자바 지원의 진화

스레드와 높은 수준의 추상화

스트림을 이용해 스레드 사용 패턴을 추상화할 수 있다

스트림을 사용하면 복잡성도 줄어든다

Executor와 스레드 풀

스레드의 힘을 높은 수준으로 끌어올렸다

스레드의 문제

  • 자바 스레드는 운영체제 스레드에 접근하는데, 운영체제 스레드를 만들고 종료하려면 비싼 비용이 들며, 운영체제 스레드는 숫자가 제한
  • 지원하는 운영체제 스레드의 수를 초과하면 어플리케이션이 죽을 수 있음

스레드 풀

  • 워커 스레드라 불리는 nThreads를 포함하는 ExecutorService를 만들고 이들을 스레드 풀에 저장
  • 스레드풀에서 순차적으로 태스크를 스레드에 배정
  • 태스크가 종료되면 스레드 반납

장점

  • 하드웨어에 맞는 수의 태스크 유지
  • 태스크가 많아도 오버헤드 없이 스레드풀에 전달 가능
  • 큐의 크기 조정, 거부 정책, 태스크 종류에 따른 우선순위 등 다양한 설정 가능

단점

  • I/O나 네트워크 연결을 기다리는 태스크가 있다면 스레드가 놀게 된다
  • 중요한 코드를 실행하는 스레드가 죽는 일이 발생하지 않도록 main이 반환하기 전에 모든 스레드의 작업이 끝가길 기다리기 떄문에 프로그램 종료하기 전에 모든 스레드 풀을 종료하는 습관을 갖아야 한다

스레드의 다른 추상화: 중첩되지 않은 메서드 호출

엄격한 포크/조인: 스레드 생성과 join()이 한 쌍처럼 중첩된 메서드 호출 내에 추가

여유로운 포크/조인: 태스크를 외부 호출해서 종료하도록 기다리는 방식

비동기 메서드: 사용자의 메서드 호출에 의해 스레드가 생성되고 메서드를 벗어나 계속 실행되는 동시성 형태

비동기 사용시 주의 사항

  • 데이터 경쟁 문제를 일으키지 않도록 주의
  • 스레드가 종료되지 않은 상황에서 자바의 main() 메서드가 반환하면 애플리케이션이 크래시되고 외부 데이터 일관성이 파괴될 수 있음

프로그램을 작은 태스크 단위로 구조화해 병렬성의 장점을 극대화 하는것이 목표다

동기 API와 비동기 API

Future 형식 API

Future<Integer> y = f(x);
Future<Integer> z = g(x);
System.out.println(y.get() + z.get());

리액티브 형식 API

void f(int x, IntConsumer dealWithResult);

결과가 준비되면 람다로 호출하는 태스크를 만든다

잠자기는 해로운 것으로 간주

스레드는 잠들어도 여전히 시스템 자원을 점유 → 다른 태스크가 시작되지 못하게 막는다

태스크에서 기다리는 일을 만들지 말거나 예외를 일으키는 방법으로 처리

비동기 API에서 예외 처리

비동기 API에서 호출된 메서드의 실제 바디는 별도의 스레드에서 호출

값이 있을때(onNext), 에러 발생(onError), 값을 소진했거나 에러가 발생해서 더이상 처리할 데이터가 없을 때(onComplete)시 콜백이 호출

이런 종류의 호출을 메시지 혹은 이벤트라 부른다

CompletableFuture와 콤비네이터를 이용한 동시성

CompletableFuture: 실행할 코드 없이 Future을 만들 수 있도록 허용하며 complete() 메서드를 이용해 나중에 어떤 값을 이용해 다른 스레드가 이를 완료할 수 있고 get()으로 갑을 얻을 수 있도록 허용

thenCombine 메서드를 사용함으로 Future 연산들이 끝났을 때 스레드 풀에서 실행된 연산을 만든다→ get을 사용하지 않아 스레드 블락을 막고 병렬 실행의 효율성 높이고 데드락을 피할 수 있다

발행-구독 그리고 리액티브 프로그래밍

Future: 한 번만 실행해 결과를 제공하는 정석적 모델

리액티브 프로그래밍: 여러 Future같은 객체를 통해 여러 결과를 제공

자바에서 리액티브 프로그래밍을 위해 발행-구독 모델을 지원

발행-구독 모델

  • 구독자가 구독할 수 있는 발행자
  • 이 연결을 구독이라 부름
  • 이 연결을 이용해 메시지를 전송

억압력

정보의 흐름 속도를 조절하는 것

다음 사항들을 고려해야 됨

  • 어떤 속도로 보낼 것인가?
  • 큐가 너무 커질때 대응
  • Subscriber가 준비가 되지 않으면 큐의 데이터를 폐기해야 하나?

당김 기반 리액티브 압력: Subscriber가 Publisher로 부터 요청을 당기는 방식인데 이렇게 억압력을 구현할 수 도 있다

리액티브 시스템 VS 리액티브 프로그래밍

리액티브 시스템

  • 런타임 환경이 변화에 대응하도록 전체 아키텍처가 설계된 프로그램
  • 반응성, 회복성, 탄력성이라는 특징을 갖는다
    • 반응성: 실시간으로 입력에 반응
    • 회복성: 한 컨포넌트의 실패로 전체 시스템이 실패하지 않음
    • 탄력성: 시스템이 자신의 작업 부하에 맞게 적응하며 작업을 효율적으로 처리

리엑티브 프로그래밍 형식을 이용해 이 특성들을 구현할 수 있다

리엑티브 프로그래밍은 메시지 주도 속성을 반영

메시지 주도 속성: 컴포넌트는 처리할 입력을 기다리고 결과를 다른 컴포넌트로 보내면서 시스템이 반응

profile
Keep going하는 개발자

0개의 댓글