Java 7의 AsynchronousFileChannel은 Java NIO에 추가되었다. AsynchronousFileChannel은 비동기적으로 파일로부터 데이터를 읽고 기록하기를 가능하게 만든다. 이 튜토리얼은 AsynchronousFileChannel의 사용방법을 설명할 것이다.
Creating an AsynchronousFileChannel
AsynchronousFileChannel을 그것의 정적 메소드인 open()을 통하여 생성한다. 다음 AsynchronousFileChannel을 생성하는 예제가 있다:
Path path = Paths.get("data/test.xml"); AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
open() 메소드의 첫번째 매개변수는 AsynchronousFileChannel과 관련된 파일을 가리키는 Path 객체이다.
두번째 매개변수는 기본 파일에서 동작되는 작업들이 무엇인지 AsynchronousFileChannel에 말해주는 하나 이상의 열기 옵션이다. 위 예제에서 파일이 읽기 위해 열릴 것을 의미하는 StandardOpenOption.READ를 사용한다.
Reading Data
두가지 방법으로 AsynchronousFileChannel으로부터 데이터를 읽을 수 있다. 각 방법은 AsynchronousFileChannel의 read() 메소드들 중 하나를 호출하여 데이터를 읽는 것이다. 데이터를 읽는 메소드 두가지 모두 다음 섹션에 포함될 것이다.
Reading Data Via a Future
AsynchronousFileChannel으로부터 데이터를 읽는 첫번째 방법은 Future
를 반환하는 read() 메소드를 호출하는 것이다. 다음은 read() 메소드를 호출하는 방법을 보여준다:
Future<Integer> operation = fileChannel.read(buffer, 0);
이 read() 메소드의 버전은 첫번째 매개변수로 ByteBuffer를 취한다. AsynchronousFileChannel으로부터 읽은 데이터는 이 ByteBuffer에 읽어진다. 두번째 매개변수는 파일의 읽기 시작하는 byte position이다.
read() 메소드는 읽기 동작이 완료되지 않았음에도 불구하고 즉시 반환한다. read() 메소드에 의해 반환된 Future 객체의 메소드인 isDone()을 호출하여 읽기 동작이 언제 완료되었는지 확인할 수 있다.
다음 read() 메소드의 해당 버전의 사용 방법을 보여주는 좀 더 긴 예제이다:
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ); ByteBuffer buffer = ByteBuffer.allocate(1024); long position = 0; Future<Integer> operation = fileChannel.read(buffer, position); while(!operation.isDone()); buffer.flip(); byte[] data = new byte[buffer.limit()]; buffer.get(data); System.out.println(new String(data)); buffer.clear();
이 예제는 AsynchronousFileChannel과 read() 메소드의 매개변수로 전달되는 ByteBuffer를 생성과 더불어 위치를 0으로 전달한다. 이 예제는 read() 호출 이후에 반환된 Future의 메소드 isDone()이 true를 반환할 때까지 반복문을 돌 것이다. 물론, 이것은 CPU 사용에 있어 매우 비효율적이지만, 어떻게든 읽기 동작이 완료 될 때까지 기다려야만 한다.
읽기 동작이 ByteBuffer에 데이터 읽기를 완료하게 되면 System.out에 문자열로 출력한다.
Reading Data Via a CompletionHandler
AsynchronousFileChannel으로부터의 데이터를 읽는 두번째 메소드는 CompletionHandler를 매개변수로 갖는 버전의 read() 메소드를 호출하는 것이다. 다음 read() 메소드를 호출을 방법이다:
fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer attachment) { System.out.println("result = " + result); attachment.flip(); byte[] data = new byte[attachment.limit()]; attachment.get(data); System.out.println(new String(data)); attachment.clear(); } @Override public void failed(Throwable exc, ByteBuffer attachment) { } });
읽기 작업이 완료되면 CompletionHandler의 completed() 메소드가 호출될 것이다. completed() 메소드에 전달되는 두번째 매개변수인 Integer는 얼만큼 bytes를 읽었는지 말해주고, "attachment"는 read() 메소드에 전달된다. "attachment"는 read() 메소드에 세번째 매개변수이다. 위 경우에선 읽어진 데이터에 ByteBuffer가 된다. 첨부하기 위한 객체에 자유럽게 접근할 수 있다.
만약 읽기 동작이 실패하면, CompletionHandler의 failed() 메소드가 대신 호출될 것이다.
Writing Data
읽기와 마찬가지로, 두가지 방법으로 AsynchronousFileChannel에 데이터를 쓸 수 있다. 각 방법은 AsynchronousFileChannel의 write() 메소드들 중 하나를 호출하는 것이다. 데이터를 기록하는 메소드 두가지 모두 다음 섹션에 포함될 것이다.
Writing Data Via a Future
AsynchronousFileChannel은 비동기적으로 데이터 기록하기도 가능하게 한다. 다음 Java AsynchronousFileChannel 기록하기 전체 예제가 있다:
Path path = Paths.get("data/test-write.txt"); AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE); ByteBuffer buffer = ByteBuffer.allocate(1024); long position = 0; buffer.put("test data".getBytes()); buffer.flip(); Future<Integer> operation = fileChannel.write(buffer, position); buffer.clear(); while(!operation.isDone()); System.out.println("Write done");
먼저 AsynchronousFileChannel은 기록 모드로 열린다. 그 후 ByteBuffer가 생성되고 일부 데이터가 그곳에 기록된다. 그리고 난 뒤 ByteBuffer의 데이터는 파일에 기록된다. 예제는 마지막으로 기록 동작이 완료된 때를 보기 위해 반환된 Future를 확인한다.
주의할 점은, 이 코드가 동작하기 이전에 저 파일이 이미 존재해야 할 것이다. 만약 파일이 존재하지 않는다면 write() 메소드는 java.nio.file.NoSuchFileException을 던질 것이다.
다음 코드로 Path가 가리키는 파일이 존재하는지 확인할 수 있다:
if(!Files.exists(path)){ Files.createFile(path); }
Writing Data Via a CompletionHandler
기록이 완료되었음을 말해주는데에 Future 대신 CompletionHandler와 AsynchronousFileChannel으로 데이터를 쓸 수도 있다. 다음 CompletionHandler와 CompletionHandler으로 데이터를 기록하는 예제가 있다:
Path path = Paths.get("data/test-write.txt"); if(!Files.exists(path)){ Files.createFile(path); } AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE); ByteBuffer buffer = ByteBuffer.allocate(1024); long position = 0; buffer.put("test data".getBytes()); buffer.flip(); fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer attachment) { System.out.println("bytes written: " + result); } @Override public void failed(Throwable exc, ByteBuffer attachment) { System.out.println("Write failed"); exc.printStackTrace(); } });
CompletionHandler의 completed() 메소드는 기록 동작의 완료되었을 때 호출 될 것이다. 만약 기록이 몇몇 이유로 실패한다면, failed() 메소드가 대신 호출 될 것이다.
attachment로 된 ByteBuffer 사용방법에 주목하기 바란다. 이 객체는 CompletionHandler 메소드들에 전달된다.
'Programming > Java NIO' 카테고리의 다른 글
Files (0) | 2015.09.15 |
---|---|
Path (0) | 2015.09.15 |
vs. IO (0) | 2015.09.15 |
Pipe (0) | 2015.09.15 |
DatagramChannel (0) | 2015.09.11 |