Fundamentals 31 min read

Master Java NIO: From Basics to High‑Performance I/O with Real‑World Examples

This article provides a comprehensive guide to Java NIO, covering its core components—Channel, Buffer, and Selector—explaining the differences from traditional IO, demonstrating file, socket, and server implementations with detailed code samples, and illustrating advanced features such as memory‑mapped files, scatter/gather, and pipe communication.

Programmer DD
Programmer DD
Programmer DD
Master Java NIO: From Basics to High‑Performance I/O with Real‑World Examples

Overview

Java NIO (New I/O) introduces three core concepts: Channel , Buffer , and Selector . Unlike traditional stream‑oriented IO, NIO operates on buffers, allowing data to be read into a buffer from a channel or written from a buffer to a channel, and enabling a single thread to manage multiple channels.

Channel

A Channel is a bidirectional conduit comparable to a stream but capable of both reading and writing. The main implementations are FileChannel, DatagramChannel, SocketChannel, and ServerSocketChannel, corresponding to file IO, UDP, and TCP (client/server) respectively.

Buffer

Buffers are containers for primitive data types (e.g., ByteBuffer, CharBuffer, IntBuffer). Typical usage follows these steps:

Allocate space (e.g., ByteBuffer buf = ByteBuffer.allocate(1024)).

Read data into the buffer ( channel.read(buf)).

Flip the buffer ( buf.flip()) to prepare for reading.

Read data from the buffer ( buf.get()).

Clear or compact the buffer for further writes.

Buffers maintain capacity , position , limit , and mark to track state.

NIO Overview
NIO Overview

Selector

A Selector allows a single thread to monitor multiple channels for events such as connection acceptance, data arrival, or readiness to write, eliminating the need for busy‑waiting loops.

Typical workflow:

Create a selector: Selector selector = Selector.open(); Register channels with interest sets (e.g., OP_ACCEPT, OP_READ).

Call selector.select() (or select(timeout)) to block until events occur.

Iterate over selected keys, handling each event (accept, read, write, connect).

FileChannel Example (Traditional IO vs NIO)

public static void method2() {
    InputStream in = null;
    try {
        in = new BufferedInputStream(new FileInputStream("src/nomal_io.txt"));
        byte[] buf = new byte[1024];
        int bytesRead = in.read(buf);
        while (bytesRead != -1) {
            for (int i = 0; i < bytesRead; i++) {
                System.out.print((char) buf[i]);
            }
            bytesRead = in.read(buf);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try { if (in != null) in.close(); } catch (IOException e) { e.printStackTrace(); }
    }
}
public static void method1() {
    RandomAccessFile aFile = null;
    try {
        aFile = new RandomAccessFile("src/nio.txt", "rw");
        FileChannel fileChannel = aFile.getChannel();
        ByteBuffer buf = ByteBuffer.allocate(1024);
        int bytesRead = fileChannel.read(buf);
        System.out.println(bytesRead);
        while (bytesRead != -1) {
            buf.flip();
            while (buf.hasRemaining()) {
                System.out.print((char) buf.get());
            }
            buf.compact();
            bytesRead = fileChannel.read(buf);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try { if (aFile != null) aFile.close(); } catch (IOException e) { e.printStackTrace(); }
    }
}

SocketChannel Example (Client)

public static void client() {
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    SocketChannel socketChannel = null;
    try {
        socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);
        socketChannel.connect(new InetSocketAddress("10.10.195.115", 8080));
        if (socketChannel.finishConnect()) {
            int i = 0;
            while (true) {
                TimeUnit.SECONDS.sleep(1);
                String info = "I'm " + i++ + "-th information from client";
                buffer.clear();
                buffer.put(info.getBytes());
                buffer.flip();
                while (buffer.hasRemaining()) {
                    socketChannel.write(buffer);
                }
            }
        }
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    } finally {
        try { if (socketChannel != null) socketChannel.close(); } catch (IOException e) { e.printStackTrace(); }
    }
}

ServerSocketChannel & Selector Example (TCP Server)

public static void selector() {
    Selector selector = null;
    ServerSocketChannel ssc = null;
    try {
        selector = Selector.open();
        ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(8080));
        ssc.configureBlocking(false);
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            if (selector.select(3000) == 0) continue;
            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                if (key.isAcceptable()) handleAccept(key);
                if (key.isReadable()) handleRead(key);
                if (key.isWritable() && key.isValid()) handleWrite(key);
                iter.remove();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try { if (selector != null) selector.close(); } catch (IOException e) { e.printStackTrace(); }
        try { if (ssc != null) ssc.close(); } catch (IOException e) { e.printStackTrace(); }
    }
}

private static void handleAccept(SelectionKey key) throws IOException {
    ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel();
    SocketChannel sc = ssChannel.accept();
    sc.configureBlocking(false);
    sc.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocateDirect(1024));
}

private static void handleRead(SelectionKey key) throws IOException {
    SocketChannel sc = (SocketChannel) key.channel();
    ByteBuffer buf = (ByteBuffer) key.attachment();
    long bytesRead = sc.read(buf);
    while (bytesRead > 0) {
        buf.flip();
        while (buf.hasRemaining()) System.out.print((char) buf.get());
        System.out.println();
        buf.clear();
        bytesRead = sc.read(buf);
    }
    if (bytesRead == -1) sc.close();
}

private static void handleWrite(SelectionKey key) throws IOException {
    ByteBuffer buf = (ByteBuffer) key.attachment();
    buf.flip();
    SocketChannel sc = (SocketChannel) key.channel();
    while (buf.hasRemaining()) sc.write(buf);
    buf.compact();
}

Memory‑Mapped Files (MappedByteBuffer)

For large files, MappedByteBuffer offers superior performance by mapping a file directly into virtual memory.

public static void method3() {
    RandomAccessFile aFile = null;
    FileChannel fc = null;
    try {
        aFile = new RandomAccessFile("src/1.ppt", "rw");
        fc = aFile.getChannel();
        long start = System.currentTimeMillis();
        MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, aFile.length());
        long end = System.currentTimeMillis();
        System.out.println("Read time: " + (end - start) + "ms");
    } catch (IOException e) { e.printStackTrace(); }
    finally { try { if (aFile != null) aFile.close(); if (fc != null) fc.close(); } catch (IOException e) { e.printStackTrace(); } }
}

Benchmark results show MappedByteBuffer reading a 5 MB file in ~2 ms versus ~12 ms with a regular ByteBuffer, and the gap widens dramatically for larger files.

Note: A mapped byte buffer remains valid until it is garbage‑collected, which means the underlying file may stay open longer than expected.

Other NIO Features

Scatter/Gather : Read from a channel into multiple buffers (scatter) or write from multiple buffers into a channel (gather).

transferFrom / transferTo : Directly transfer bytes between channels without intermediate buffers.

Pipe : One‑way data connection between two threads (source and sink channels).

DatagramChannel : UDP communication using send/receive methods.

These utilities enable efficient, non‑blocking, and scalable I/O operations suitable for high‑performance server applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaNIOchannelbufferselectorFileChannelSocketChannel
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.