dev-sohee 님의 블로그
Java NIO(New Input/Output)는 왜 추가되었나 본문
I/O(Input/Output)은 파일을 쓰거나 불러올때 또는 서버에 보낼때 입출력을 통칭하는 용어입니다.
그런데 JDK 1.4부터 NIO라는 것이 추가되었습니다.
이 글에서는 NIO가 왜 추가된 것인지, I/O와 어떤 점이 다른지 알아보겠습니다.
먼저 I/O와 비교했을 때 NIO가 다른 점을 표로 살펴보겠습니다.
# 입출력 방식
스트림(Stream)
: 데이터가 들어온 순서대로 흘러다니는 단방향의 통로
입구를 InputStream, 출구를 OutputStream이라 합니다. 스트림을 통해 데이터는 byte 형태로 흘러다닙니다.
채널(Channel)
: 데이터가 흘러다니는 양방향의 통로
채널은 양방향이기 때문에 스트림처럼 Input과 Output을 구별하여 따로 만들 필요가 없습니다. 기본적으로 버퍼를 통해서만 read와 write를 할 수 있는 버퍼 방식입니다.
# 버퍼 방식
버퍼(Buffer)
: 임시로 데이터를 담아둘 수 있는 일종의 큐
바이트 단위의 데이터가 입력될 때마다 즉시 전송하는 스트림은 디스크 접근이나 네트워크 접근 같은 오버헤드가 발생할 수 있기 때문에 비효율적입니다. 버퍼는 중간에서 입력을 모아서 한번에 출력함으로써 I/O의 성능을 향상시킵니다.
버퍼가 출력되는 주요 경우는 아래와 같습니다.
1. 버퍼가 가득 찬 경우
2. 프로그래머가 직접 flush() 함수를 호출하여 버퍼에 있는 데이터를 강제로 출력
3. 프로그램 종료
4. 특정 버퍼 정책: 파일이나 네트워크와 같은 I/O 작업에서 사용하는 버퍼의 경우, 특정 조건에 따라 버퍼링 정책이 다르게 설정될 수 있습니다. 예를 들어, 파일 입출력에서는 일정한 크기의 데이터 블록이 쌓일 때마다 출력될 수 있습니다.
# 동기/비동기 방식
동기(Synchronous)
: 작업을 요청한 후, 결과를 받을 때까지 요청한 측(스레드 또는 프로세스)이 기다리는 방식
여기서 "기다림"은 작업이 완료될 때까지 다른 작업을 하지 않는다는 의미입니다. 즉, 호출하는 함수가 작업이 완료될 때까지 제어권을 유지합니다.
- 장점: 코드가 이해하기 쉽고, 동작이 직관적입니다.
- 단점: 자원이 비효율적으로 사용됩니다. 예를 들어, 네트워크 통신이나 파일 읽기/쓰기 같은 작업에서 스레드가 블로킹되어 자원을 낭비하게 됩니다.
비동기(Asynchronous)
: 작업을 요청한 후, 결과를 받을 때까지 요청한 측이 기다리지 않고 다른 작업을 계속 수행하는 방식
작업이 완료되면 별도의 메커니즘(예시) 콜백 함수 또는 이벤트 핸들러)을 통해 완료 통지를 받습니다. 비동기 작업은 호출하는 함수가 작업을 위임하고 제어권을 즉시 반환할 수 있습니다.
- 장점: 자원을 효율적으로 사용할 수 있습니다. 많은 수의 클라이언트를 동시에 처리할 때 유리합니다.
- 단점: 코드가 복잡해질 수 있으며, 디버깅이 어려울 수 있습니다.
# Blocking/Non-Blocking
Blocking
: 함수 호출이 완료될 때까지 호출한 측(스레드 또는 프로세스)이 대기하는 방식
블로킹은 동기/비동기와는 별개로, 요청한 작업이 완료될 때까지 호출이 차단된다는 의미입니다.
- 장점: 코드가 이해하기 쉽고, 관리가 용이합니다.
- 단점: 성능 저하가 발생할 수 있습니다. 특히, 네트워크 통신에서 대기 시간이 길어지면 자원이 비효율적으로 사용됩니다. 많은 수의 클라이언트를 처리할 때 비효율적일 수 있습니다.
Non-Blocking
: 함수 호출이 즉시 반환되어 호출한 측이 대기하지 않는 방식
즉, 작업이 완료되었는지 여부와 관계없이 제어권을 즉시 반환합니다.
- 장점: 자원의 효율적인 사용이 가능하고, 많은 수의 클라이언트를 동시에 처리할 때 유리합니다.
- 단점: 코드가 복잡해질 수 있으며, 디버깅이 어려울 수 있습니다.
동기/비동기와 Blocking/Non-Blocking은 언뜻 보면 비슷해보일 수 있지만 엄연히 다른 개념입니다. 위의 내용을 요약하면,
동기/비동기는 작업의 완료 통지 시점과 방식에 초점을 맞춥니다.
- 동기: 작업 완료 시 직접 통지하고 호출자가 완료를 기다립니다.
- 비동기: 작업 완료 후 별도로 통지하고 호출자가 완료를 기다리지 않습니다.
Blocking/Non-Blocking은 함수 호출 시점의 제어권 반환 여부에 초점을 맞춥니다.
- 블로킹: 호출자가 작업 완료까지 대기합니다.
- 논블로킹: 호출자가 제어권을 작업 완료 여부와 관계없이 즉시 반환합니다.
이해를 돕기 위해 4가지 경우에 대해 예시를 들어 설명하겠습니다.
1. Sync-Blocking : 사용자가(호출자) 수강신청 버튼을 클릭하고(Thread1) 아무것도 하지 않고 수강신청 결과만 기다리고 있습니다.
2. Sync-NonBlocking : 사용자가(호출자) 수강신청 버튼 클릭 후 대기 팝업 창이 떴는데 해당 페이지(Thread1)에서 다른 메뉴(ex)학사일정)으로 이동하고(NonBlocking) 다른 페이지는 열지 않습니다(Sync).
3. Async-Blocking : 사용자가(호출자) 수강신청 버튼을 클릭했는데 대기 창이 떴다면 해당 페이지(Thread1)는 놔둔 채(Blocking) 다른 페이지(Thread2)를 열어서 다른 사이트에 접속합니다(Async).
4. Async-NonBlocking : 사용자가(호출자) 수강신청 버튼 클릭 후 대기 팝업 창이 떴는데 해당 페이지(Thread1)에서 다른 메뉴(ex)학사일정)으로 이동하고(NonBlocking) 다른 페이지(Thread2)를 추가로 열어서 다른 사이트에 접속합니다(Async).
오늘 다룬 내용은 동시성과 병렬 처리의 이해에 중요한 역할을 합니다. Blocking과 Non-blocking의 차이를 이해하면 자원 사용의 효율성을 높일 수 있고 이를 통해 다수의 작업을 병렬로 처리함으로써 시스템의 처리량을 높일 수 있습니다.
이 개념을 깊이 이해하고 적절하게 적용하면 고성능 네트워크 어플리케이션이나 대규모 데이터를 처리하는 시스템에서 효율적인 시스템을 개발할 수 있을 것입니다.
'java' 카테고리의 다른 글
멀티 스레드 동시성 문제 해결 전략 (Redisson 분산락, Volatile, CAS 알고리즘) (1) | 2024.08.03 |
---|---|
Java 8, Java 11, Java 17 뭐가 달라졌는지 알고 사용하자 (0) | 2024.07.28 |
JVM의 청소부, GC(Garbage Collector) (0) | 2024.07.13 |
JVM의 부스터, JIT-Compiler (0) | 2024.07.13 |
인터페이스와 추상 클래스, 둘 중 뭘 써야 하는거지? (0) | 2024.07.06 |