skip to content

Name the main channel families and explain which support non-blocking mode and selectors, and which do not.

level: middleimportance: should knowfreq 40%

answer

  1. FileChannel, SocketChannel/ServerSocketChannel, DatagramChannel, Pipe channels
  2. SelectableChannel = socket/datagram/pipe → non-blocking + Selector
  3. FileChannel is always blocking, never selectable
  4. configureBlocking(false) + register(selector, OP_*)
  5. Async file I/O → AsynchronousFileChannel (NIO.2)

basics

~20 s

The main channel families are FileChannel (files), SocketChannel and ServerSocketChannel (TCP), and DatagramChannel (UDP). The socket and datagram channels can be set to non-blocking and registered with a Selector so one thread handles many connections. FileChannel is always blocking and cannot be used with a Selector.

solid answer

~40 s

The common channel families are: FileChannel for file I/O; SocketChannel (a TCP client connection) and ServerSocketChannel (accepts incoming TCP connections); DatagramChannel for UDP; and Pipe.SourceChannel/SinkChannel for in-process pipes. The dividing line is SelectableChannel: SocketChannel, ServerSocketChannel, DatagramChannel, and the Pipe channels extend SelectableChannel, so they can be put in non-blocking mode via configureBlocking(false) and registered with a Selector to be multiplexed by a single thread reacting to readiness events (OP_READ, OP_WRITE, OP_ACCEPT, OP_CONNECT). FileChannel is NOT a SelectableChannel — it is always blocking and cannot be registered with a Selector. This is a deliberate design choice: file I/O readiness doesn't fit the readiness-selection model the way socket readiness does (a regular file is always 'ready'). For asynchronous file I/O you use the NIO.2 AsynchronousFileChannel instead, and for high-throughput file moves you use FileChannel's transferTo/mmap.

code

java · 11 lines
java
// Non-blocking server socket registered with a selector
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(8080));
server.configureBlocking(false);                 // required before register
server.register(selector, SelectionKey.OP_ACCEPT);

// FileChannel cannot do this:
// FileChannel fc = FileChannel.open(path);
// fc.configureBlocking(false);   // no such option — always blocking
// fc.register(selector, ...);    // FileChannel is not a SelectableChannel

go deeper

for a junior

Can name FileChannel, SocketChannel, and DatagramChannel and knows sockets can be non-blocking while files are not.

for a middle

Knows which channels are SelectableChannels, can set non-blocking mode and register with a Selector, and lists the interest ops.

for a senior

Explains why FileChannel is never selectable, contrasts non-blocking NIO with NIO.2 async channels, and sketches a reactor loop.

for a principal

Chooses between reactor (NIO selector), proactor (NIO.2 async), and thread-per-connection models for a given workload and reasons about their scalability and complexity trade-offs.

## The channel families | Channel | I/O entity | Direction | Selectable? | |---|---|---|---| | `FileChannel` | regular file | read+write (bidirectional) | **No** — always blocking | | `SocketChannel` | TCP connection (client side / accepted) | read+write | **Yes** | | `ServerSocketChannel` | TCP listening socket | accept only | **Yes** | | `DatagramChannel` | UDP socket | read+write (send/receive) | **Yes** | | `Pipe.SourceChannel` / `Pipe.SinkChannel` | in-JVM pipe | read / write | **Yes** | ## Blocking vs non-blocking — what it means In **blocking** mode, a `read`/`write`/`accept`/`connect` call *waits* until it can make progress: a blocking `read` parks the thread until data arrives. Simple, but it ties up one thread per connection. In **non-blocking** mode (`channel.configureBlocking(false)`), the same calls return *immediately*: `read` returns whatever is available right now (possibly `0`), `accept` returns `null` if no connection is pending, `connect` returns `false` and you finish later with `finishConnect()`. This lets a single thread juggle many channels without parking on any one of them. ## SelectableChannel and the Selector The abstract class **`SelectableChannel`** is the family of channels that can be **multiplexed** by a **`Selector`**. The flow: 1. `channel.configureBlocking(false)`. 2. `SelectionKey key = channel.register(selector, ops)` where `ops` is a bitmask of interest operations: `OP_READ`, `OP_WRITE`, `OP_ACCEPT` (server sockets), `OP_CONNECT` (connecting client). 3. A single thread loops on `selector.select()`, which blocks until one or more registered channels are **ready** for an operation, then returns the ready `SelectionKey`s. 4. The thread services each ready channel (read/write/accept) without blocking. This **one-thread-many-connections** model (a reactor) is the foundation of scalable servers (Netty, etc.) and is why NIO sockets exist. ## Why FileChannel is NOT selectable `FileChannel` does **not** extend `SelectableChannel`; it is **always blocking** and you cannot `configureBlocking(false)` it or register it with a `Selector`. The reason is conceptual: the readiness model asks "is this channel ready to make progress without blocking?" For a network socket that's meaningful — data may or may not have arrived. But a **regular file on disk is essentially always 'ready'**: a read will return data (the OS may still block on disk, but there's no readiness event to wait for). So selection doesn't fit. Attempting to register a `FileChannel` throws `IllegalBlockingModeException`/`ClassCastException`-style errors at compile or runtime because it isn't a `SelectableChannel`. ## What you use instead for async file I/O NIO.2 (Java 7) adds **`AsynchronousFileChannel`** and **`AsynchronousSocketChannel`**, which return `Future`s or invoke `CompletionHandler`s — a different (proactor) model for true async file operations. For throughput on files you reach for `FileChannel.transferTo`/`transferFrom` (zero-copy) and `FileChannel.map` (memory mapping) rather than selectors. ## Quick decision guide - Many concurrent sockets, one/few threads → non-blocking `SocketChannel`/`ServerSocketChannel` + `Selector`. - File reads/writes → `FileChannel` (blocking); add `transferTo`/`map` for performance. - Callback/future-based async file or socket → NIO.2 `Asynchronous*Channel`.

  • Why can't a FileChannel be registered with a Selector?
    FileChannel doesn't extend SelectableChannel and is always blocking. The readiness-selection model doesn't fit files because a regular file is conceptually always ready to read/write — there's no readiness event to wait on like there is for socket data arrival.
  • What are the interest operations you register with a Selector?
    OP_READ (data available to read), OP_WRITE (channel can accept writes), OP_ACCEPT (a ServerSocketChannel has a pending connection), and OP_CONNECT (a connecting SocketChannel finished connecting). They're combined as a bitmask.

saying these in an interview costs you the question

  • Claiming FileChannel can be non-blocking or registered with a Selector
  • Thinking a Selector copies data (it only reports readiness)
  • Confusing non-blocking NIO with asynchronous NIO.2 (Asynchronous*Channel)
  • Forgetting configureBlocking(false) before registering a socket channel

context