chapter 9. 비동기 채널 API
기본 인터페이스는 java.nio.channels.AsynchronousChannel 임.
주요 하위 클래스
- AsynchronousFileChannel
- AsynchronousServerSocketChannel
- AsynchronousSocketChannel
- AsynchronousDatagramChannel (최종 버젼에서 제거됨 - 베타 까지 존재함.)
비동기 채널 그룹 - AsynchronousChannelGroup 클래스
9.1 동기 I/O와 비동기 I/O의 비교
목적
동기 I/O - 간단하게 요청을 하나 보내고 응답을 하나 받고 싶을 때 , 연결당 스레드 하나, 성능, 확장성이 제한됨
비동기 I/O - 성능과 확장성 좋음. 매우 많이 시간이 걸리는 I/O는 비동기로 최적화 할 수 있다.
** 즉 매우 긴 시간 걸리는 동작은 비동기 , 짧은 시간 걸리는 동작은 동기로 하면 좋음.
9.2 비동기 I/O의 큰 그림
자바에서 비동기I/O은 비동기 채널을 말함.
모든 비동기 I/O은 시작하고 완료되었을 때 통지를 받는다. -> 중요한 규칙
비동기 I/O의 형식
- 미처리 결과(Pending result)
- 완료 결과(Complete result)
9.2.1 미처리 결과와 Future 클래스
java.util.concurrent.Future<V> 객체는 비동기I/O 작업의 미처리 결과를 나타냄. Future의 메소드를 통해서 작업 결과(완료,대기중)을 알수 있다.
Future 메소드
- isDone() : 작업 완료 여부
- isCancelled() : 작업 취소 여부
- cancel() : 명시적으로 작업 취소 하기 (성공하면 true를 반환)
- get() , get(long,TimeUnit) : 결과가 준비될 때까지 혹은 주어진 시간 만큼 대기
타입 아웃되면 TimeoutException이 발생한다.
비동기 I/O 작업을 취소 특징
- 대기중에 모든 쓰레드 CancellationException을 발생한다.
- 올바르게 취소된다는 보장은 할 수 없음. 최소한 I/O 작업이 시작하는 것은 막을 수있음.
- cancel(true)를 사용하면 채널을 닫아서 I/O 작업이 중단 될 수 있음.
- 이 채널의 외부 I/O는 AsychronousCloseException이 발생
- 취소된 읽기/쓰기 작업과 관련된 I/O 버퍼는 채널이 열려 있어도 더 이상 접근하지 못하게 됨.
9.2.2 완료 결과와 CompletionHandler 인터페이스
콜백 매커니즘 방법, 비동기 작업에 콜백을 등록해두고 작업이 완료되거나 실패하면 핸들러(CompletionHanndler) 호출한다.
완료 핸들러 형식은 CompletionHanndler<V,A> 이면 V - 반환값 , A - I/O 작업에 첨부된 객체의 타입. A를 통해서 어느 작업이 완료 되었는지 알 수 있다. ** 쓰레드가 유지되는 것을 피하기 위해서 빠른 시간에 완료해야 함.
CompletionHanndler의 주요 메소드
- completed() : 완료되었을 때 호출됨.
- failed() : 작업이 실패 했을 때 호출됨.
9.2.3 비동기 채널의 유형 (3가지)
- AsynchronousFileChannel : 파일 읽기,쓰기, 조작을 위한 비동기 채널
- AsynchronousServerSocketChannel : 서버(TCP) 소켓 위한 비동기 채널
- AsynchronousSocketChannel : 클라이언트 소켓 위한 비동기 채널
AsynchronousFileChannel 채널 작업 시 발생하는 예외들
- AsynchronousCloseException - close() 메소드를 사용하여 닫을 때 발생함.
- ClosedChannelException - close() 메소드를 사용하여 닫은 후 다른 작업 하려고 하면 발생함.
- NonReadableChannelException - 읽기 준비가 되어 있지 않을 때 읽기 시도 할때
- NonWritableChannelExcepiton - 쓰기 준비가 되어 있지 않을 때 쓰기 시도 할때
- OverlappingFileLockException - 자바 가상머신에 의해서 잠금이 되어 있는데 잠금을 시도할 때
AsynchronousServerSocketChannel 채널 작업 시 발생하는 예외들
- AsynchronousCloseException - close() 메소드를 사용하여 소켓 채널을 닫을 때 발생함.
- ClosedChannelException - close() 메소드를 사용하여 닫은 후 다른 작업 하려고 하면 발생함.
- ShutdownChannelGroupException - 채널 그룹이 닫혀 있는데 열기를 시도 할때
- NotYetBoundException - 바인딩되지 않은 채널에 대해서 accept() 메소드를 호출할 때
- AcceptPendingException - 이전의 수락 처리가 완료되기 전에 어떤 쓰레드가 수락 처리를 시작하려 할때.
AsynchronousSocketChannel 채널 작업 시 발생하는 예외들 및 주의 사항
- AsynchronousCloseException - close() 메소드를 사용하여 소켓 채널을 닫을 때 발생함.
- ClosedChannelException - close() 메소드를 사용하여 닫은 후 다른 작업 하려고 하면 발생함.
- NotYetConnectedException - 연결되지 않은 채널에 대해 I/O 연산을 호출할 때
- ReadPendingException - 이전의 읽기 처리가 완료되기 전에 어떤 쓰레드가 읽기 처리를 시작하려 할 때
- WritePendingException - 이전의 쓰기 처리가 완료되기 전에 어떤 쓰레드가 쓰기 처리를 시작하려 할 때
- AlreadyConnectedException - 채널이 이미 연결돼 있는데 연결하려 할 때
- ConnectionPendingException - 이 채널에 대해 연결 작업이 이미 진행 중인데 채널에 연결하려 할 때
- AsynchoronousSocketChannel에서 read(),write() 메소드는 읽기,쓰기 처리를 시작할 때 타임 아웃을 지정할 수 있다. 작업이 완료되기 전에 타임아웃이 경과하면 InterruptedByTimeoutException 예외가 작업을 완료한다. 일관되지 않은 상태에서 타임 아웃은 채널을 무시 할 수도 있다. 구현에서 바이트를 채널에 기록하는 것이나 읽어오는 것을 보증하지 않는다면 구현체는 채널을 구현체 한정 에러 상태로 둘 수 있다. 이 상태에서 읽기나 쓰기 처리를 시작하려 하면 지정되지 않은 런타임 예외가 발생한다.
9.2.4 그룹
AsynchronousChannelGroup 클래스 - 비동기 채널 그룹 개념을 위한, 채널의 위한 모든 스레드가 공유하는 리소스와 스레드 풀을 캡슐화 함. 채널을 실제 적으로 그룹이 소유하고 있고 채널을 닫으면 그룹도 닫힌다.
기본 그룹
- 개발자가 생성하는 그룹 외에 JVM에서도 자동으로 생선된, 시스템 전체에 적용되는 기본 그룹
- 간단한 애플리케이션 유용
- 그룹을 지정하지 않거나 null을 전달하면 기본그룹에 속한다.
- 기본 그룹과 관련 시스템 속성(System.getProperty)
- 첫번째 속성 - “java.nio.channels.DefaultThreadPool.threadFactory”,이 속성은 팩토리 객체를 생성할 때 사용하는 풀 클래스 이름이 들어 있으면 이 이름 생성된 객체의 newThread() 메소드를 사용해서 각 쓰레드 생성할 때 사용한다.
- 두번째 속성 - “java.nio.channels.DefaultThreadPool.initialSize : 쓰레드 풀의 초기 크기(숫자형이어야 한다.)
사용자 지정 그룹
자신 용도에 맞는 그룹을 생성할 수 있다. AsychronousChannelGroup에서 제공하는 세 가지 메소드를 사용함.
- 고정 스레드 풀 - public static AsynchronousChannelGroup withFixedThreadPool(int nThreads, ThreadFactory threadFactory) throws IOException
- 캐시 스레드 풀 - public static AsynchronousChannelGroup withCachedThreadPool(ExecutorService executor, int initialSize) throws IOException
- 내부적으로 히든 쓰레드 풀을 사용해서 작업 한다.
- OutOfMemory가 발생할 수 있다.(발생 조건은 모든 쓰레드가 잠기고 큐에 계속해서 쌓일 때) - 그러므로 모니터링을 해야 한다.
- 지명 스레드 풀 - public static AsynchronousChannelGroup withThreadPool(ExecutorService executor) throws IOException
- ExecutorService 사용하는 방식 , ExecutorServie 설정 할 때 주의 해야 함. 즉
- 주의 사항 하나. 실행할 작업의 언바운딩 큐잉이나 다이렉트 핸드오프를 지원해야 한다.-> 무슨 소리인겨 그냥 번역 한것.
- 주의 사항 둘. 작업(task)을 직접 호출하기 위해 쓰레드가 execute() 메소드를 호출하는 것을 허용해서는 안된다. -> 이해 가면 천재
그룹 종료 메소드 :
- shutdown() , shutdownNow() 메소드를 사용. 만약 채널을 생성하려 하면 ShutdownChannelGroupException 예외가 발생한다.
- shutdown() - 실행 중인 스레드를 강제로 중지하거나 인터럽트 하지 않는다.
- shutdownNow() - 그룹을 강제 종료 한다.
- awaitTermination() : 메소드에 타임아웃을 지정해서 호출하면 그룹이 종료될 때 까지 블록할 수있다.
- isTerminated() : 그룹이 종료됐는지 확인 가능
- isShutdown() : 종료됐는지 확인 가능
** ByteBuffer는 쓰레드 안정서이 없다. 작업중인 바이트 버퍼에 접근하지 않게 해야 한다. 이 문제해결 하기 위해서 ByteBuffer 풀을 사용한는 것이다. 메모리 관련 문제도 해결 가능함.
9.2.5 ExecutorService API 소개
간단하게 설명하면 사용자 지정 스레트 풀을 만들 수 있는 편리한 방법을 제공함.
일반적으로 결과를 반환하지 않는 Runnable사용하지만 만약 결과를 받고 싶다면 java.util.concurrent.Callable를 사용할 수 있다.-> 결과는 Callable.call() 메서드의 내부에서 계산(재정의 할 수 있음). submit() 제출, 미처리 결과는 Future를 반환한다.
유용한 팁 - 만약 제품용으로 개선 하려면
- 바이프 버퍼 풀을 사용하고 읽기 작업을 조절하라 -
- 짧은 읽기 작업에만 블로킹을 사용하라
- FIFO 큐를 사용하고, 쓰기 작업에 블로킹을 허용하라