자바

자바 NIO 파일 비동기 채널 - AsynchronousFileChannel 생성과 닫기, 파일 읽기 , 파일 쓰기

알통몬_ 2017. 4. 17. 09:23
반응형


안녕하세요 알통몬입니다.

공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^

 


파일 비동기 채널 :

FileChannel 의 read() 와 write() 는 파일의 입출력 동안에 블로킹됩니다.

UI 변경이나 이벤트를 처리하는 스레드에서 위 두 개의 메소드를 호출하면 블로킹되는 동안 

UI 갱신이나 이벤트 처리가 불가능합니다.

때문에 별도의 작업 스레드를 생성해 위 메소드들을 호출해야 합니다.

그리고 동시에 처리해야 할 파일 수가 많으면 스레드 수도 증가를 하기 때문에 문제가 됩니다.

=> java NIO는 불특정 다수의 파일이나 대용량 파일의 입출력 작업을 위해 비동기 파일 채널을 제공합니다.



AsynchronousFileChannel 의 특징 :

파일의 데이터 입출력을 위해 read() 나 write() 를 호출하면 스레드풀에게 작업 처리 요청을 하고

위 메소드들은 즉시 리턴합니다. 실질적인 입출력 작업 처리는 스레드풀의 작업 스레드가 담당합니다.

작업 스레드가 파일 입출력을 완료하게 되면 콜백 메소드가 자동 호출됩니다. 

때문에 작업 완료 후 실행해야 할 메소드가 있다면 콜백 메소드에 작성하면 됩니다.


AsynchronousFileChannel 생성과 닫기

생성은 두 가지 정적 open()을 호출하여 얻을 수 있습니다.

-

AsynchronousFileChannel fileChannel = 

      AsynchronousFileChannel.open( Path path, OpenOption... options)

위 코드로 생성된 AsynchronousFileChannel 은 내부적으로 생성되는 기본 스레드풀을 이용해

스레드를 관리합니다. 때문에 기본 스레드풀의 최대 스레드 수를 개발자가 지정할 수 없기 때문에

아래 처럼 생성할 수도 있습니다.


-

AsynchronousFileChannel fileChannel =

        AsynchronousFileChannel.open( 

                             Path file, // file 의 매개 값은 Path 객체입니다.

                             Set<? extends OpenOptions> options, // 열기 옵션 값들이 저장될 객체입니다.

                             ExecutorService service, // 스레드풀인 ExecutorService 객체입니다.

                             FileAttribute<?>... attrs // 파일 생성 시 파일 속성 값이 될 FileAttribute를 나열합니다.

);


예)

"C:\Temp\file.txt" 파일에 대한 AsynchronousFileChannel  생성

ExecutorService executorService = Excutors.newFixedThreadpool(

    Runtime.getRuntime().availableProcessors() // CPU 코어 수 리턴합니다.

  );


AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(

  Paths.get("C:\Temp\file.txt"),

  EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE), // 매개값으로

  나열된 열거 상수를 Set 객체에 리턴합니다.

  executorService

 );

AsynchronousFileChannel 을 더 이상 사용하지 않을 경우 close()메서드 호출해서 닫아줍니다.

fileChannel.close();


파일 읽기와 파일 쓰기

AsynchronousFileChannel 이 생성되었다면 read() 와 write()를 이용해 입출력이 가능합니다.

read(ByteBuffer dst, long position, A attachment, CompletionHandler<Integer, A> handler);

write(ByteBuffer src, long position, A attachment, CompletionHandler<Integer, A> handler);

위 메소드들은 호출하는 즉시 리턴되고 스레드풀의 스레드가 입출력 작업을 수행합니다.

dst 와 src 매개 값은 읽거나 쓰기위한 ByteBuffer,  position 매개 값은 파일에서 읽을 위치 또는 쓸 위치입니다. attachment 매개 값은 콜백 메소드로 전달할 첨부 객체 입니다. => 콜백 메소드에서 결과 값 이외에

추가적인 정보를 얻고자 할 때 사용하는 객체로, 첨부 객체가 없을 경우는 null을 대입하면 됩니다.

handler 매개 값은 CompletionHandler< Integer, A>를 저장하는데

Integer 는 입출력 작업의 결과 타입으로 read() 나 write() 가 읽거나 쓴 바이트 수 입니다.

A는 첨부 객체 타입으로 CompletionHandler 구현 객체를 작성할 때 임의로 지정 가능하며

첨부 객체가 필요 없다면 A = Void를 지정하면 됩니다.

CompletionHandler<Integer, A> 구현 객체는 비동기 작업이 정상적으로 완료된 경우와

예외 발생으로 실패한 경우에 자동으로 콜백되는 두 메소드를 가지고 있어야 합니다.


completed() 의 result 매개 값은 작업 결과가 대입되는데, read() 와 write() 작업 결과는 읽거나 쓴 

바이트 수입니다.

attachment 매개 값은 read() 와 write() 호출 시에 제공된 첨부 객체입니다.

failed()의 exc 매개 값은 작업 처리 도중 발생한 예외입니다.

** 콜백 메소드를 실행하는 스레드는 read() 나 write() 를 호출한 스레드가 아니고, 스레드풀의

작업 스레드입니다.

=> javaFX 애플리케이션의 경우에 UI 성성 및 변경 작업을 이 메소드에서 직접할 수 없고

Platform.runLater()를 이용해야 합니다.

ex) CompletionHandler 구현 클래스 작성 방법

new CompletionHandler<Integer, A>() {

  @Override

  public void completed(Integer result, A attachment){ ... }

  @Override

  public void failed(Throwable exc, A attachment) { ... }

};


예제)

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.AsynchronousFileChannel;

import java.nio.channels.CompletionHandler;

import java.nio.charset.Charset;

import java.nio.file.Files;

import java.nio.file.Path;

import java.nio.file.Paths;

import java.nio.file.StandardOpenOption;

import java.util.EnumSet;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;


public class Example {

 public static void main(String[] args) throws Exception {

  //스레드풀 생성

  ExecutorService executorService = Executors.newFixedThreadPool(

   Runtime.getRuntime().availableProcessors() //CPU 코어 개수 리턴

  );

  

  for(int i=0; i<10; i++) {

   Path path = Paths.get("C:/Temp/file" + i + ".txt");

   Files.createDirectories(path.getParent());

   

   //비동기 파일 채널 생성

  AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(

   path, 

   EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE),

   executorService

   );  

   

   Charset charset = Charset.defaultCharset();

   ByteBuffer byteBuffer = charset.encode("안녕~");

   

   //첨부 객체 생성

   class Attachment {

    Path path;

    AsynchronousFileChannel fileChannel;

   }

   Attachment attachment = new Attachment();

   attachment.path = path;

   attachment.fileChannel = fileChannel;

   

   //CompletionHandler 객체 생성   

   CompletionHandler<Integer, Attachment> completionHandler = 

    new CompletionHandler<Integer, Attachment>() {

    @Override

    public void completed(Integer result, Attachment attachment) {

     System.out.println(attachment.path.getFileName() + " : " + result + " bytes written : " + Thread.currentThread().getName());

     try { attachment.fileChannel.close(); } catch (IOException e) {}

    }

    @Override

    public void failed(Throwable exc, Attachment attachment) {

     exc.printStackTrace();

     try { attachment.fileChannel.close(); } catch (IOException e) {}

    }

   };

   

   fileChannel.write(byteBuffer, 0, attachment, completionHandler);

  }

  

  //스레드풀 종료

  executorService.shutdown();

 }

}


이상입니다.

다음 포스팅에서는 TCP 블로킹 채널에 대해 공부하겠습니다.

반응형