Blocking and Non-blocking I/O

2022. 6. 4. 15:47개발공부/Java

Blocking and Non-blocking I/O

NIO는 데이터들을 보관할 클래스들을 정의하고, 데이터를 블록들 내에서 처리하는 방식으로써 low-level 최적화의 이점을 가집니다.

I/O란 컴퓨터와 세상을, 하나의 프로그램과 컴퓨터를 연결해주는 인터페이스 입니다. 대부분의 I/O가 실제로 운영체제에 내장되어 있는 것은 컴퓨터 시스템에 매우 중요한 요소입니다.

모든 I/O는 1 byte들을 하나씩 Stream으로 읽어야했습니다. 이는 객체를 byte로 변환하거나, 반대로 byte를 객체로 변환해주는 기능을 했습니다.

NIO는 I/O와 동일한 역할과 목적을 가지지만, Stream과 달리 block I/O라는 메타포를 사용합니다. 나중에도 나오겠지만, block I/O는 stream I/O보다 효율적입니다.

NIO를 사용해야하는 이유?

NIO는 Java 프로그래머들이 I/O를 사용할 때 네이티브 코드를 사용하지 않고도 빠른 속도를 낼 수 있도록 하도록 만들어졌습니다. NIO는 시간이 가장 많이 소요되는 I/O 작업들(buffers를 채우고 비우는
작업들)를 운영체제에 위임합니다. 이로써 속도가 보장됩니다.

Streams versus blocks

The most important distinction between the original I/O library (found in java.io.* ) and NIO has to do with how data is
packaged and transmitted.

Stream는 byte를 하나씩 읽고 쓰기 때문에 streamed된 데이터의 필터들을 생성하기 용이합니다. 반대로, I/O는 느립니다.

Channels and buffers

버퍼란 공급자와 소비자가 다른 속도로 공급/소비할 때 사용되는 자료구조입니다. 예를 들어 사탕봉지에서 하나씩 그릇에 내어놓고 먹는다면 그릇이 버퍼의 기능을 한다고 볼 수 있습니다.

채널이란 데이터를 읽어서 쓸 수 있게 해주는 객체입니다. 앞서 말했던 stream과 흡사합니다. 채널엔 직접 데이터를 쓰지 않고, 버퍼에 데이터를 쓴다음 읽을때도 채널에서 버퍼를 읽어 데이터를 가져옵니다. 하지만
채널은 stream과 달리 양방향이 가능합니다. 데이터 읽기와 쓰기 둘다 가능하기 때문에, InputStream, OutputStream 둘 중에 하나의 하위클래스인 stream과 차이가 있습니다.

양방향이란 특성을 가진 채널은 운영체제의 현실적인 부분들을 stream보다 더 잘 반영할 수 있습니다.(단방향인 경우가 적다는 얘기 같습니다.) 예를 들어 유닉스 모델에서 운영체제 체널들은 양방향식으로 되어있다고
합니다.

From theory to practice: Reading and writing in NIO

  • Reading

public void nioIoChannelReadBuffer()throws IOException{
        //input stream을 채널로 만든다.
        FileInputStream fis=new FileInputStream("readandshow.txt");
        FileChannel fc=fis.getChannel();

        //버퍼를 생성한다.
        ByteBuffer buffer=ByteBuffer.allocate(1024);
        //채널이 버퍼를 읽어온다.
        fc.read(buffer);
        }
  • Writing

public void nioIoChannelWriteBuffer(byte[]message)throws IOException{
        //output stream에서 채널을 가져온다.
        FileOutputStream fos=new FileOutputStream("writesomebytes.txt");
        FileChannel fc=fos.getChannel();

        //버퍼를 생성한다.
        ByteBuffer buffer=ByteBuffer.allocate(1024);

        //버퍼에 메시지(바이트)를 담는다.
        for(byte b:message){
        buffer.put(b);
        }
        buffer.flip();

        //채널에 버퍼를 쓴다.
        fc.write(buffer);
}

참고자료