Java blocking IO 와 non blocking IO 에 대해서..

이동명·2023년 6월 1일
1
post-thumbnail

blocking IO

  • 블로킹 I/O는 입출력 작업이 완료될 때까지 현재 실행 흐름이 차단(block)되는 방식입니다. 입출력 작업이 시작되면 해당 작업이 완료될 때까지 다른 작업을 수행할 수 없습니다. 이러한 차단은 입출력 작업이 완료되기 전까지 스레드가 대기하며, 작업이 완료되면 스레드는 결과를 반환하고 다음 작업을 수행합니다.

  • 예를 들어, InputStream의 read() 메서드는 데이터를 읽을 때까지 현재 스레드를 차단합니다. 데이터가 도착할 때까지 스레드는 아무 작업도 수행하지 않고 대기합니다. 이러한 블로킹 방식은 간단하고 사용하기 쉽지만, 스레드가 입출력 작업을 기다리는 동안 블로킹되기 때문에 동시에 여러 클라이언트의 요청을 처리하기에는 비효율적입니다.

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class BlockingIOExample {
    public static void main(String[] args) {
        try {
            InputStream inputStream = new FileInputStream("input.txt");
            byte[] buffer = new byte[1024];
            int bytesRead = inputStream.read(buffer);  // 블로킹 호출

            while (bytesRead != -1) {
                // 읽은 데이터 처리
                System.out.write(buffer, 0, bytesRead);
                bytesRead = inputStream.read(buffer);  // 블로킹 호출
            }

            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

위의 코드에서 inputStream.read(buffer) 메서드는 파일에서 데이터를 읽을 때까지 블로킹됩니다. 즉, 데이터가 도착하기 전까지는 다음 코드로 진행되지 않습니다. 이는 블로킹 I/O의 특징입니다.

non blocking IO

  • 논블로킹 I/O는 입출력 작업이 완료되지 않더라도 현재 실행 흐름이 차단되지 않는 방식입니다. 입출력 작업을 시작한 후에 다른 작업을 수행할 수 있으며, 입출력 작업이 완료되면 그 결과를 확인할 수 있습니다. 이를 위해 자바에서는 NIO(Non-blocking I/O) 패키지를 제공하며, Selector, Channel, ByteBuffer 등의 요소를 사용하여 구현합니다.

  • 논블로킹 I/O는 단일 스레드에서 여러 개의 연결을 처리하는 데 유용합니다. 단일 스레드가 여러 개의 채널을 관리하고, 입출력 작업이 완료되지 않더라도 다른 작업을 수행할 수 있습니다. 이는 입출력 작업을 기다리는 동안 다른 클라이언트의 요청을 처리하는 데 유용하며, 동시 접속이 많은 서버 애플리케이션에 적합합니다.

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NonBlockingIOExample {
    public static void main(String[] args) {
        try {
            Selector selector = Selector.open();
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("example.com", 80));
            socketChannel.register(selector, SelectionKey.OP_CONNECT);

            while (true) {
                selector.select();

                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectedKeys.iterator();

                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();

                    if (key.isConnectable()) {
                        SocketChannel channel = (SocketChannel) key.channel();

                        if (channel.isConnectionPending()) {
                            channel.finishConnect();
                        }

                        channel.register(selector, SelectionKey.OP_WRITE);
                    } else if (key.isWritable()) {
                        SocketChannel channel = (SocketChannel) key.channel();
                        String message = "Hello, Server!";
                        ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
                        channel.write(buffer);
                        channel.register(selector, SelectionKey.OP_READ);
                    } else if (key.isReadable()) {
                        SocketChannel channel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        channel.read(buffer);
                        buffer.flip();
                        while (buffer.hasRemaining()) {
                            System.out.print((char) buffer.get());
                        }
                        System.out.println();
                        channel.close();
                    }

                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

위의 코드는 논블로킹 소켓 채널을 사용하여 서버와 통신하는 예시입니다. selector.select() 메서드는 채널의 이벤트를 감지하고 해당 이벤트에 따라 처리합니다. 논블로킹 방식에서는 입출력 작업이 완료되지 않아도 다른 작업을 수행할 수 있으며, SelectionKey의 상태에 따라 적절한 작업을 수행합니다.

위의 예시에서는 SelectionKey.OP_CONNECT, SelectionKey.OP_WRITE, SelectionKey.OP_READ 등의 이벤트를 사용하여 입출력 작업을 비블로킹으로 처리합니다. 이렇게 논블로킹 I/O를 사용하면 단일 스레드에서 여러 개의 연결을 처리할 수 있습니다.

profile
Web Developer

0개의 댓글