본문 바로가기

개발기술/Java

Java Blocking I/O vs non-Blocking I/O

1.. JDBC (Database Access)

java
복사편집
String name = jdbcTemplate.queryForObject("SELECT name FROM users WHERE id = ?", String.class, 1);

🔒 Blocking behavior:

  • The current thread sends the SQL query over the network to the DB
  • The thread waits (blocks) until the DB returns a response
  • No other work happens on that thread until the DB reply is received

📌 JDBC is a blocking API, always


🔸 2. RestTemplate (HTTP API calls)

java
복사편집
String response = restTemplate.getForObject("https://api.example.com/data", String.class);

🔒 Blocking behavior:

  • Opens an HTTP connection
  • Sends the request
  • Waits (blocks) until the remote server responds
  • During this time, the thread is not doing anything else

📌 RestTemplate is also blocking (as it wraps classic HttpURLConnection or Apache client)


🔸 3. File I/O (Files.readAllBytes, FileInputStream, etc.)

java
복사편집
byte[] data = Files.readAllBytes(Paths.get("data.txt"));

🔒 Blocking behavior:

  • Calls into OS via read() syscall
  • If the data isn’t immediately ready, the thread is put to sleep
  • OS wakes it up when the disk read is done

📌 Java’s java.io.* is blocking I/O by design

 

 

 

TaskJava (classic)Node.js (libuv)Java (NIO/WebFlux)

File read Blocking Async callback (uses thread pool) Async callback
Socket Blocking Non-blocking (epoll) Non-blocking (selector)
DB Query Blocking (JDBC) Non-blocking (with some drivers) Non-blocking (R2DBC)

 

 

 

ConceptJava NIONode.js / libuv

Multiplexer abstraction Selector libuv internal poll mechanism
Register a file/socket channel.register(selector, ...) uv_poll_t, uv_read_start, etc.
Wait for I/O readiness selector.select() epoll_wait() inside poll phase
I/O readiness event SelectionKey (e.g., OP_READ) Callback to JS handler (socket.on('data'))
Event loop processor Your thread calling .select() libuv's event loop (uv_run())
Underlying OS call epoll, kqueue, IOCP epoll, kqueue, IOCP (same!)

 

 

 

 

Java NIO is designed to be cross-platform, so it chooses the appropriate OS-level mechanism depending on the platform you're running on.

 

Under the hood: What Java NIO uses

OSJava NIO uses...Notes
Linux ✅ epoll Efficient, scalable, edge-triggered
macOS / BSD ✅ kqueue High-perf polling system
Windows ✅ IOCP (I/O Completion Port) Uses special selector provider
Solaris ✅ /dev/poll or poll Older but still functional

 

How does Java know what to use?

When you call:

java
복사편집
Selector selector = Selector.open();

Internally, the JVM uses a platform-specific SelectorProvider:

 

 

 

 

 

  • For BIO, all methods are blocking and directly call OS syscalls (read, write, accept, etc.)
  • For NIO, methods are non-blocking, and readiness is monitored via:
    • epoll on Linux
    • kqueue on macOS
    • IOCP on Windows
  • FileChannel.read() is blocking, even though it’s part of java.nio — because files don’t support readiness

 

Let’s build the table like this:

 

I/O Task Category Java BIO Method OS Call (BIO) Java NIO Method OS Call (NIO)  epoll Applicable?
File Read FileInputStream.read() read() FileChannel.read() read() ❌ No (regular files always "ready")
File Write FileOutputStream.write() write() FileChannel.write() write() ❌ No
Memory Mapping ❌ N/A ❌ N/A FileChannel.map() mmap() ❌ No (direct memory access)
File Open new FileInputStream() open() / openat() FileChannel.open() open() / openat() ❌ No
Socket Accept ServerSocket.accept() accept() ServerSocketChannel.accept() accept() via epoll ✅ Yes
Socket Read Socket.read() read() SocketChannel.read() read() via epoll ✅ Yes
Socket Write Socket.write() write() SocketChannel.write() write() via epoll ✅ Yes
Socket Connect Socket.connect() connect() SocketChannel.connect() connect() via epoll ✅ Yes
UDP Send/Recv DatagramSocket.receive() recvfrom() DatagramChannel.read() recvfrom() via epoll ✅ Yes
Pipe Read PipedInputStream.read() read() Pipe.SourceChannel.read() read() via epoll ✅ Yes (on Linux)
Pipe Write PipedOutputStream.write() write() Pipe.SinkChannel.write() write() via epoll ✅ Yes (on Linux)