[JAVA] BufferedReader 의 Blocking

Bellmin·2025년 4월 13일

Java

목록 보기
2/11
post-thumbnail

입출력 스트림

자바에는 바이트 기반 스트림과 문자 기반 스트림이 존재한다.

  • 바이트 기반 스트림 : 바이트 단위로 이루어진 데이터를 입/출력 할 때
  • 문자 기반 스트림 : 문자(2바이트 이상)로 이루어진 데이터를 입/출력 할 때

입출력 스트림을 공부하면서 헷갈렸던 것은, 바이트 기반 스트림이었다.

바이트 기반 스트림은 문자 데이터 (=텍스트 데이터)가 아닌 사진이나 동영상 등의 이진 데이터로 구성된 데이터를 입출력 할 때 사용한다.

Stream , Reader , Writer

Stream

스트림은 InputStream , OutputStream 이 가장 기본적이고 원시 스트림이다.
(모든 입출력 스트림은 InputStream 과 OutputStream 으로부터 파생된다.)

또한 접두사로 Stream 이 붙은 클래스는 대부분 바이트 기반이라고 보면 된다.

ex) FileOutputStream , FileInputStream, BufferedInputStream 등

Reader, Writer

문자 기반 스트림인 Reader 와 Writer는 문자 데이터의 입/출력을 다루는 최상위 추상 클래스이다.

마찬가지로 Reader 와 Writer 가 접두사로 붙은 클래스는 문자 기반이라고 보면 된다.

ex) InputStreamReader , OutputStreamReader

BufferedReader

오늘은 여러 입출력 스트림 클래스 중에서도 BufferedReader에 알아보려고 한다.
BufferedReader는 '버퍼화된 Reader'의 의미로 버퍼를 사용하여 입출력을 빠르게 할 수 있도록 만들어졌다.

그리고 실제로 BufferedReader 를 사용했을 때, 다른 InputStream 이나 InputStreamReader 를 사용했을 때보다 속도가 빠르다.

BufferedReader 는 한 번의 IO 호출로 일정량의 데이터를 읽어와서 메모리에 저장한다.

그리고 여러 번의 읽기 작업에서 이 메모리 버퍼에서 데이터를 가져오기 때문에, 직접적으로 InputStreamReader를 사용할 때마다 발생하는 시스템 호출 비용을 줄일 수 있다.

readline()

	public String readLine() throws IOException {
        return readLine(false, null);
    }
    
    String readLine(boolean ignoreLF, boolean[] term) throws IOException {
        StringBuilder s = null;
        int startChar;

        synchronized (lock) {
            ...

        bufferLoop:
            for (;;) {

                if (nextChar >= nChars) // 현재 버퍼의 인덱스가, 버퍼에 저장된 문자의 길이보다 클 경우
                    fill(); // 버퍼에 새로운 데이터를 채운다.
                if (nextChar >= nChars) { /* EOF */
                    if (s != null && s.length() > 0)
                        return s.toString();
                    else
                        return null;
                }
                
                ...
                

            charLoop: // 버퍼에 저장된 문자들을 읽는 Loop 블록이다.
                for (i = nextChar; i < nChars; i++) {
                    c = cb[i];
                    if ((c == '\n') || (c == '\r')) {
                        if (term != null) term[0] = true;
                        eol = true;
                        break charLoop;
                    }
                }

                startChar = nextChar;
                nextChar = i;

                if (eol) { // end of line 이 나올 경우, 리턴한다.
                    String str;
                    if (s == null) {
                        str = new String(cb, startChar, i - startChar);
                    } else {
                        s.append(cb, startChar, i - startChar);
                        str = s.toString();
                    }
                    nextChar++;
                    if (c == '\r') {
                        skipLF = true;
                    }
                    return str;
                }

                if (s == null)
                    s = new StringBuilder(defaultExpectedLineLength);
                s.append(cb, startChar, i - startChar);
            }
        }
    }
    

BufferedReader의 readline 메소드의 동작을 잠깐 살펴보자.

  1. readline 메소드가 호출되면, BufferedReader 내부에 있는 버퍼에서 가져올 데이터가 있는지 확인한다.

  2. 가져올 데이터가 없으면, fill() 메소드를 호출하여, 기본 입력 스트림으로부터 일정량의 데이터를 가져와 버퍼에 채운다.

  3. 버퍼에 채운 데이터들 중에서 개행문자를 찾는다. (개행문자는 운영체제 종류마다 다르다. 하지만 다 지원한다.)

  4. 개행문자를 찾으면 찾은 개행문자 까지의 문자열을 추출하여 반환한다.

readline() 메소드는 이름에서 알 수 있듯이, 한 줄의 데이터를 읽어들여서 반환한다.

그러나..

보통 readline() 메소드를 많이 사용하는데, 이 메소드는 Blocking 방식으로 동작한다.

어디서 블로킹이 될까 궁금해서 생각해 보았는데, 두 가지의 후보가 있었다.

  1. 버퍼에 데이터가 없을 경우 readLine 자체에서 블로킹
  2. 버퍼에 데이터가 없어서, fill() 하는 과정에서 블로킹

찾아본 결과 2번이 맞다.

(위에서 말했지만)
BufferedReader 는 기본적으로, 버퍼에 데이터가 없을 경우 fill() 메소드를 호출하여 기본 스트림으로부터
데이터를 읽는다.

이때 fill() 내부에서 기본스트림의 read() 를 호출하는데, 이 read()가 Blocking 방식이라서

readline() 메소드가 블로킹 방식인 것이다.

(후즈인에서는 readLine() 메소드를 호출하여, 읽어들일 데이터가 없을 경우 바로 반환하도록 NonBlocking 방식으로 다시 구현했다.)

Non Blocking IO

BufferedReader는 Blocking 방식으로 동작하므로, 입력이 주어지지 않을 경우에 무한정 대기하여 잘못하면 교착 상태가 발생할 수 있다.

이를 방지하는 방법 중 하나는, Non Blocking IO 를 사용하는 것인데 자바에서는 공식적으로 java NIO (non-blocking IO) 패키지를 지원하고 있다.

자세한 활용법은 다음 시간에 알아보도록 하자.

0개의 댓글