Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java AsynchronousFileChannel: own the buffer through completion

Last updated: 5 Oct 20265 min read
tutorial
AdvancedBy AITrove Editorial

AsynchronousFileChannel.read starts an operation and returns a Future for its byte count. The destination ByteBuffer belongs to that operation until completion is observed.

Operational contract

The method opens a channel, starts one positioned read, waits for completion, and then extracts exactly the returned bytes. Waiting on Future.get blocks this caller, so the example demonstrates the buffer and channel lifetime rather than claiming a throughput gain. A truly concurrent workflow can submit multiple independent reads with separate buffers and a bounded admission policy. A completed read may contain fewer bytes than requested; a negative count means end of file. On interruption, the method restores the thread's interrupt flag and closes the channel; do not reuse the buffer while an unfinished operation might still reference it.

Failure case

A review service requests up to 47,000 bytes from an archive. It must not hand the same buffer to another read just because the Future was returned. The code waits, then copies the actual byte count. If the service needs an exact record, it uses a loop or the positioned exact-reader lesson rather than padding the short result.

Java code

Java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class ArchiveAsyncRead {
    public static byte[] readOnce(Path archive, long offset, int capacity)
            throws IOException, ExecutionException, InterruptedException {
        if (offset < 0 || capacity < 0 || capacity > 47_000) {
            throw new IllegalArgumentException("Invalid read range");
        }
        ByteBuffer destination = ByteBuffer.allocate(capacity);
        try (AsynchronousFileChannel channel = AsynchronousFileChannel.open(
                archive, StandardOpenOption.READ)) {
            Future<Integer> pending = channel.read(destination, offset);
            int count;
            try {
                count = pending.get();
            } catch (InterruptedException interrupted) {
                Thread.currentThread().interrupt();
                throw interrupted;
            }
            if (count < 0) return new byte[0];
            destination.flip();
            byte[] received = new byte[count];
            destination.get(received);
            return received;
        }
    }
}

Performance and ownership cost

One read requests at most C bytes and retains O(C) buffer memory plus O(R) returned bytes for R bytes received. Future.get waits for completion; the asynchronous API alone does not make this call nonblocking. A pipeline of concurrent operations needs a bound on outstanding buffers and file handles.

Common Mistakes

  • Do not reuse a destination buffer before completion.
  • Do not treat one completed read as an exact-length record.
  • Do not claim nonblocking behavior when the caller immediately waits on Future.get.

Connected lessons

java
file channel boundaries
asynchronousfilechannel-buffer-lifetime
Storage details