The CompletionHandler
is an interface used for handling the result of an asynchronous I/O operation (like AsynchronousFileChannel
or AsynchronousSocketChannel
).
Interface Definition
public interface CompletionHandler<V, A> {
void completed(V result, A attachment);
void failed(Throwable exc, A attachment);
}
Code language: PHP (php)

Program 1: Simple Asynchronous File Read
LotusJavaPrince wants to read a file asynchronously using AsynchronousFileChannel
, and Mahesh is tasked with writing the result to the console when the operation completes successfully or fails.
import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.file.*; import java.util.concurrent.*; public class SimpleFileReadAsync { public static void main(String[] args) throws IOException { Path path = Paths.get("sample.txt"); // Open AsynchronousFileChannel AsynchronousFileChannel asyncChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ); // Allocate buffer ByteBuffer buffer = ByteBuffer.allocate(1024); // Start async read asyncChannel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() { public void completed(Integer result, ByteBuffer attachment) { System.out.println("Read completed by Mahesh! Bytes read: " + result); attachment.flip(); byte[] data = new byte[attachment.limit()]; attachment.get(data); System.out.println("Data: " + new String(data)); } public void failed(Throwable exc, ByteBuffer attachment) { System.out.println("Read failed! Error: " + exc.getMessage()); } }); System.out.println("Read operation initiated by LotusJavaPrince..."); // Give time for async read to complete try { Thread.sleep(2000); } catch (InterruptedException e) {} } }
Problem Statement
LotusJavaPrince runs a bank logging system. Logs are written asynchronously by Mahesh every few seconds. Simultaneously, an admin interface reads logs asynchronously to detect errors. Use AsynchronousFileChannel
and CompletionHandler
for both operations.
import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.file.*; import java.util.concurrent.*; public class LogSystemAsyncCaseStudy { static Path logFile = Paths.get("bank_logs.txt"); public static void main(String[] args) throws IOException, InterruptedException { // Ensure the file exists if (!Files.exists(logFile)) Files.createFile(logFile); ExecutorService executor = Executors.newFixedThreadPool(2); // Start asynchronous log writing (by Mahesh) executor.submit(() -> { try (AsynchronousFileChannel writerChannel = AsynchronousFileChannel.open(logFile, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND)) { for (int i = 1; i <= 5; i++) { String log = "Log Entry " + i + " from Mahesh\n"; ByteBuffer buffer = ByteBuffer.wrap(log.getBytes()); writerChannel.write(buffer, writerChannel.size(), buffer, new CompletionHandler<Integer, ByteBuffer>() { public void completed(Integer result, ByteBuffer attachment) { System.out.println("Mahesh: Log written successfully (" + result + " bytes)"); } public void failed(Throwable exc, ByteBuffer attachment) { System.err.println("Mahesh: Failed to write log - " + exc.getMessage()); } }); Thread.sleep(1500); // Simulate delay between logs } } catch (Exception e) { e.printStackTrace(); } }); // Start asynchronous log reading (by LotusJavaPrince) executor.submit(() -> { try (AsynchronousFileChannel readerChannel = AsynchronousFileChannel.open(logFile, StandardOpenOption.READ)) { Thread.sleep(2000); // Wait for initial logs ByteBuffer buffer = ByteBuffer.allocate(1024); readerChannel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() { public void completed(Integer result, ByteBuffer attachment) { System.out.println("LotusJavaPrince: Log read successful. Bytes: " + result); attachment.flip(); byte[] data = new byte[attachment.limit()]; attachment.get(data); System.out.println("Logs Read:\n" + new String(data)); } public void failed(Throwable exc, ByteBuffer attachment) { System.err.println("LotusJavaPrince: Failed to read logs - " + exc.getMessage()); } }); } catch (Exception e) { e.printStackTrace(); } }); executor.shutdown(); executor.awaitTermination(10, TimeUnit.SECONDS); } }
Benefits of Using CompletionHandler
- Non-blocking: Main thread is not tied up waiting for results.
- Efficient resource usage: No need to spawn a thread per request.
- Event-driven: Promotes a clean callback-based programming model.
- Better concurrency: Suitable for highly concurrent systems like servers.
Example Use Cases
- Logging system: Write logs to a file asynchronously and use
completed()
to archive them. - Web server: Use
CompletionHandler
withAsynchronousSocketChannel
to manage multiple client connections without using multiple threads. - File uploader: Read a file asynchronously and alert the user when done using a callback.
Integration with Other APIs
CompletionHandler
works well with:
ExecutorService
: You can assign a custom thread pool for handling I/O callbacks.Future
andCompletableFuture
: Can be used in combination to bridge blocking and async code.- Java 21+ structured concurrency (in newer JDKs) to coordinate asynchronous tasks more predictably.
The CompletionHandler
interface is at the core of Java’s asynchronous programming model for I/O. It enables callback-based execution for file and network operations, improving performance, responsiveness, and scalability in I/O-intensive applications.
It is especially valuable in:
- Web servers
- Cloud apps
- Desktop apps that require responsive UIs
- Microservices and reactive architectures