1. NIO와 비동기 프로그래밍 개념
- *NIO (New Input/Output)**는 Java 1.4에서 도입된 새로운 입출력 API로, 특히 네트워크 프로그래밍에서 효율성을 높이기 위해 설계되었습니다. NIO의 주요 특징은 비동기적이고 논블로킹(Non-blocking) I/O를 지원한다는 것입니다.
- 논블로킹 I/O: 전통적인 I/O는 블로킹 방식으로 동작합니다. 예를 들어, 데이터가 준비될 때까지 스레드가 대기해야 합니다. 반면, NIO의 논블로킹 I/O는 데이터가 준비되지 않아도 스레드가 블로킹되지 않으며, 다른 작업을 수행할 수 있습니다.
- 비동기 프로그래밍: 비동기 프로그래밍은 작업이 완료될 때까지 기다리지 않고, 작업이 완료되면 콜백 또는 미래 객체를 통해 결과를 처리하는 방식입니다. 비동기 방식은 효율적인 자원 사용과 높은 응답성을 제공합니다.
2. 단일 스레드 vs 멀티 스레드
멀티스레드 프로그래밍은 각 요청을 별도의 스레드로 처리하여 병렬로 작업을 수행하는 방식입니다. 그러나 이 방식은 여러 가지 단점이 있습니다:
- 컨텍스트 스위칭: 멀티스레드 환경에서는 CPU가 여러 스레드 사이에서 작업을 전환해야 합니다. 이 전환 과정에서 컨텍스트 스위칭(Context Switching)이 발생하며, 이는 성능 오버헤드(추가 작업 비용)를 초래합니다.
- 자원 소비: 각 스레드는 일정량의 메모리와 CPU를 소모합니다. 많은 수의 스레드를 생성하면 시스템 자원이 크게 소비되며, 성능이 저하될 수 있습니다.
반면에, 단일 스레드 비동기 프로그래밍은 하나의 스레드로 여러 작업을 처리할 수 있습니다. 이 방식에서는 비동기적으로 작업이 완료되기를 기다리면서, 스레드가 다른 작업을 처리할 수 있습니다.
- 컨텍스트 스위칭 감소: 단일 스레드에서는 컨텍스트 스위칭이 거의 발생하지 않으므로, 오버헤드가 줄어들어 성능이 향상됩니다.
- 효율성: 논블로킹 I/O와 비동기 처리 덕분에 단일 스레드로도 많은 작업을 효율적으로 처리할 수 있습니다. 이는 특히 I/O 바운드 작업(예: 네트워크 요청 처리)에 매우 유리합니다.
3. 비동기 처리와 효율성
비동기 처리가 가능해지는 이유는 논블로킹 I/O와 단일 스레드의 조합 덕분입니다. 비동기 처리에서는 작업이 완료될 때까지 대기하지 않고, 작업이 완료되면 콜백 함수나 미래 객체 등을 통해 결과를 처리합니다. 이렇게 하면 스레드가 불필요하게 대기하는 시간을 최소화할 수 있습니다.
정리
- I/O 작업은 시간이 많이 걸리는 작업입니다.
- 예를 들어, 하드디스크에 데이터를 저장하거나 파일을 읽는 작업은 시간이 많이 소요됩니다. 네트워크 통신도 마찬가지로 시간이 걸리는 I/O 작업에 해당됩니다.
- 멀티스레드 환경의 문제점:
- 멀티스레드 환경에서는 여러 스레드가 동시에 실행되지만, 각 스레드가 I/O 작업을 기다리는 동안에도 CPU는 다른 스레드를 처리하기 위해 컨텍스트 스위칭을 수행해야 합니다.
- 컨텍스트 스위칭은 스레드 간 전환에 소요되는 시간으로, 이 과정이 반복되면 시스템 자원이 소모되고, 성능이 저하될 수 있습니다.
- 단일 스레드와 논블로킹 I/O:
- 단일 스레드로 운영하는 경우, 스레드 관리의 복잡성이 줄어들고 컨텍스트 스위칭의 오버헤드가 발생하지 않습니다.
- 논블로킹 I/O는 스레드가 I/O 작업을 수행하는 동안 블로킹되지 않게 하여, 스레드가 다른 작업을 계속 수행할 수 있게 합니다.
- 이 방식에서는 I/O 작업이 완료되었을 때, 콜백 또는 이벤트를 통해 결과를 받아 처리합니다. 이로써 스레드가 대기 시간 없이 효율적으로 여러 작업을 처리할 수 있습니다.
- 비동기 처리:
- 비동기 처리란, 특정 작업이 완료되기를 기다리지 않고, 다른 작업을 먼저 수행하는 방식입니다. I/O 작업을 비동기적으로 처리하면, 작업이 완료되었을 때 그 결과를 받아 처리할 수 있습니다.
- 이는 AJAX에서 사용하는 방식과 유사합니다. AJAX는 클라이언트에서 비동기적으로 서버에 요청을 보내고, 서버가 응답을 보내면 그 결과를 받아서 처리합니다.

이벤트 루프와 Promise의 동작 과정
- I/O 작업 요청:
- 코드에서 I/O 작업(예: 파일 읽기, 네트워크 요청 등)이 발생하면, 이 작업은 비동기적으로 처리됩니다.
- 이 작업과 관련된 Promise 객체가 생성됩니다. 이때, Promise의 상태는
Pending
(대기 중)입니다.
- I/O 작업 진행:
- I/O 작업은 백그라운드에서 진행됩니다. 이 작업은 CPU와 직접적인 관계가 없으며, 하드디스크, 네트워크 장치 등에서 처리됩니다.
- 이 동안 이벤트 루프는 다른 작업을 계속 수행합니다.
- I/O 작업 완료:
- I/O 작업이 완료되면, 해당 작업과 연결된 Promise의 상태가
Fulfilled
(이행됨)로 변경됩니다. - 만약 I/O 작업이 실패했다면, Promise의 상태는
Rejected
(거부됨)로 변경됩니다.
- 이벤트 루프의 역할:
- 이벤트 루프는 계속해서 처리할 작업이 있는지 확인합니다. CPU가 다른 작업을 처리하고 있을 때는 이 작업이 끝날 때까지 기다립니다.
- CPU가 다른 작업을 마치고 비어있는 상태가 되면, 이벤트 루프는 대기 중인 Promise를 확인합니다.
- Promise 처리:
- 이벤트 루프가
Fulfilled
상태의 Promise를 확인하면, 해당 Promise에 연결된then()
메서드가 호출되어 결과를 처리합니다. - 만약 Promise가
Rejected
상태라면,catch()
메서드가 호출되어 에러를 처리합니다.
결론
단일 스레드와 논블로킹 I/O의 조합은 성능을 향상시키기 위해 설계된 방식입니다. 이 방식은 AJAX와 유사한 비동기 처리 방식을 사용하여, 시간이 오래 걸리는 I/O 작업을 효율적으로 관리할 수 있습니다. 이를 통해 멀티스레드 방식에서 발생하는 오버헤드를 줄이고, 단일 스레드로도 높은 성능을 발휘할 수 있게 됩니다.
Share article