CompletionHandler

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 with AsynchronousSocketChannel 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 and CompletableFuture: 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
Scroll to Top