AsynchronousFileChannel.read starts an operation and returns a Future for its byte count. The destination ByteBuffer belongs to that operation until completion is observed.
Java AsynchronousFileChannel: own the buffer through completion
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
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 positioned FileChannel.read: detect a short record without moving the cursor
- Java CompletableFuture: composition, failures and executor ownership
- Java cancellation: timed waits and cooperative interruption
- Java FileChannel.write: finish the buffer before reporting success
- Java FileChannel.transferTo: verify the byte count on every pass
- Java FileChannel.force: distinguish written bytes from forced storage
- Java FileLock: coordinate one byte range with a shared protocol
- Java FileChannel.truncate: cut only after validating the recovery offset
- Java file channels and JVM observations quiz
- Advanced Java
