▸JAVA/기본 문법

외부 데이터 입출력_java.nio [3/3]

코데방 2019. 12. 16.
728x90

[ java.nio 패키지 ]

  • New IO라는 의미로 java.io의 업그레이드 버전
  • NIO2는 Java Se-7 버전에서 java.nio가 한 번 더 업그레이드 되었다는 의미
  • 굳이 NIO1, NIO2의 구분 없이 그냥 NIO를 한통으로 생각하면 됨

[ 기존 Java.io와 차이점 ]

  • 양방향 Channel 방식을 사용해 외부 데이터와 입출력 연동 (통로가 하나만 있으면 됨)
  • 기본적으로 버퍼(Buffer)를 사용해 속도를 높임 (커널 버퍼를 직접 사용해서 입출력 속도 향상도 가능)
  • 비동기 지원 (메소드 호출 시점과 결과 출력 시점이 다름)
  • Non-Blocking 지원 (I/O를 수행하는 동안 스레드가 놀지 않도록(block 당하지 않도록) 함)

NIO는 어렵습니다. NIO만 정리해도 책이 한권 나올 것 같으니 이번 글에서는 가장 기초적인 수준에서 대략적인 개념만 정리해보겠습니다. 추후 다른 개념들을 모두 정리한 후 조금 더 깊게 다루도록 하겠습니다. 간단히 말하면 그냥 구 IO에서 위의 4가지 기능을 추가해둔 버전이라고 보면 됩니다. (근데 그 세부 기능들이 심하게 많습니다.)

 

 


 

아래는  NIO를 이용한 파일 입출력을 예로 설명했습니다. NIO를 통한 네트워크 입출력은 아래 링크를 참조하시면 됩니다. 사실 NIO는 파일 입출력보다 네트워킹에서 더 효율적으로 사용될 수 있습니다. 파일 입출력은 어차피 논블로킹이 지원되지 않아서 버퍼를 사용하는 정도의 효과밖에 없기 때문입니다. 버퍼는 일반 스트림에서도 사용 가능합니다.

 

2020/02/04 - [JAVA/기본 문법] - 네트워크_소켓(Socket) 통신_NIO 입출력 [2/3]

 

 

[ Java.nio.Path / Java.nio.Files 클래스 ]

  • java.io.File 클래스를 대체할 수 있는 파일 시스템 작업 기능
  • 메소드가 워낙 많고 사용법도 복잡해서 간단한 작업은 java.io.File 클래스 사용이 편함
  • java.io.File 인스턴스와 java.nio.Path 인스턴스는 서로 타입 변환 메소드를 제공

java.io에서는 File 클래스에서 경로와 파일을 다루는 기능이 모두 포함돼 있었습니다. 그러나 똑똑한(나쁜..) 자바 개발자들이 NIO를 업그레이드(NIO2)하면서 java.nio.file 패키지에 경로를 다루는 "Path" 클래스와 파일과 디렉토리를 다루는 "Files"클래스로 나눠서 다시 만들었습니다. 물론 파일 시스템에 대한 안정성과 기능도 확장되었습니다. 그리고 기존 java.io.File 클래스와도 연동해서 사용할 수 있습니다. Files 클래스는 static 메소드가 엄청 많은데다가 사용법도 어려워서 복잡한 파일 시스템 작업이 많을 때 주로 사용하게 될 것 같습니다. 간단한 건 그냥 java.io.File 클래스를 사용해도 무방할 듯합니다.

 

주요 메소드 및 사용법은 아래 링크글을 참조하시면 됩니다.

2019/12/16 - [JAVA/라이브러리(API)] - java.nio.file.Path 주요 메소드 [1/1]

2019/12/16 - [JAVA/라이브러리(API)] - java.nio.file.Files 주요 메소드 [1/1]

 


 

[ 채널생성 (Channel) ]

  • java.io의 단방향 스트림 개념과 달리, 양방향 통로를 채널이라고 함
  • 하드웨어, 장비, 파일, 네트워크 소켓 등과 입출력 작업을 수행할 수 있는 통로
  • 하나의 채널만 열어두면 입출력을 동시에 진행할 수 있음
  • 기존 스트림 방식 대비 속도가 빠름 (통로 자체가 빠른 속도로 동작함)
  • non-blocking 방식을 지원하여 자원 사용의 효율성을 높임

외부 데이터 입출력을 위해서는 어찌됐든 통로를 생성해야 합니다. 기존 java.io가 input, output이라는 단방향 스트림 방식으로 통로를 생성했다면 java.nio에서는 채널이라는 양방향 통로를 생성해서 사용합니다. 일단 하나만 생성해도 돼서 편리한 점도 있고, 저레벨 언어가 가진 기술을 적용해서 속도도 기존 대비 빨라졌다고 합니다.

 

또한 blocking 방식으로 작동하던 스트림과는 달리, non-blocking 방식이 지원되어 보다 효율적으로 리소스를 사용할 수 있도록 했습니다. non-bloking이란, 스레드가 입출력 작업을 할 때 스레드를 멈추지 않고 여러 입출력 작업을 동시에 할 수 있도록 하는 것을 말합니다. 참고로 FileChannel 클래스는 non-blocking이 지원되지 않아 대용량 입출력 위해서는 다중 스레드를 이용해야 합니다. NIO를 이용한 non-blocking 방식의 네트워크 입출력은 위의 링크글을 참조하시면 됩니다.

 

생성은 아래와 같이 해줄 수도 있고 java.io의 클래스들에서 제공하는 getChannel() 메소드를 사용해서 만들어줄 수도 있습니다. 다만 getChannel() 메소드를 사용하면 스트림과 같이 단방향 채널이 생성됩니다. 옵션은 기본 Enum(열거형) 클래스 중 StandardOpenOption의 상수를 입력해줍니다. 자세한 사용법은 아래 링크글을 참조하시면 됩니다.

 

2019/12/17 - [JAVA/라이브러리(API)] - java.nio 패키지 사용법(Channel / Buffer / Charset) [1/1]

 

package study.first;

import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class Main {
	public static void main(String[] args) {

		// 채널 생성 (읽기, 쓰기모드)
		Path input = Paths.get("c:\\input.txt");
		try (FileChannel in = FileChannel.open(input, 
				StandardOpenOption.READ, StandardOpenOption.WRITE)) {			
			
			
		} catch (Exception e) {
			
			System.out.println("예외가...!");
		}		
	}
}

 

이렇게 채널을 하나 생성했으면 해당 채널에서 아래의 Buffer 클래스들 또는 Files 클래스에서 제공하는 메소드를 통해서 입출력 작업을 하면 됩니다. 

 


 

[ java.nio.Buffer 인터페이스를 상속받는 버퍼 클래스들 ]

  • java.io 필터 스트림의 버퍼 클래스들과 같이 버퍼를 사용해서 파일 입출력을 사용한다고 볼 수 있음
  • allocateDirect() 메소드를 사용하면 시스템의 커널 버퍼를 사용할 수 있어 입출력 속도 향상
  • boolean 타입을 제외한 원시타입의 버퍼 클래스들이 존재

ByteBuffer 클래스의 allocateDirect() 메소드로 생성하는 버퍼 외에는 모두 커널 버퍼가 아닌 JVM 내에 생성되는 버퍼입니다. 따라서 버퍼 사용 방식은 java.io 필터 스트림의 버퍼 클래스를 사용하는 것과 비슷합니다. 대신 위에서 언급한 차이점은 여전히 존재합니다.

 

커널 버퍼란 운영체제가 관리하는 메모리 영역에 생성되는 버퍼 공간입니다. 자바의 특성 상 외부 데이터를 가져올 때는 OS의 메모리 버퍼에 먼저 담았다가 JVM 내의 버퍼로 한번 더 데이터를 옮겨줘야 하기 때문에 메모리를 직접 다루는 C언어에 비해 입출력이 느릴 수밖에 없습니다. 이러한 단점을 개선하기 위해 나온 기능이 ByteBuffer 클래스에서 제공하는 allocateDirect() 메소드입니다. 내부적으로는 C언어를 호출해 시스템 메모리 영역을 사용한다고 합니다. 이 때문에 입출력 속도 자체는 빠르지만 내부적인 과정이 복잡하기 때문에 버퍼공간을 생성하고 해제하는 속도가 느려집니다. 따라서 커널 버퍼 사용은 한 번 만들어서 오래 사용해야할 때 활용하는 것이 좋습니다.

 

 

자세한 사용법은 아래 링크글을 참조하시면 됩니다.

 

2019/12/17 - [JAVA/라이브러리(API)] - java.nio 패키지 사용법(Channel / Buffer / Charset) [1/1]

 

하나의 채널로 파일에서 내용을 읽고 추가로 쓰기까지 가능

package study.first;

import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class Main {
	public static void main(String[] args) {

		String str = "";

		// 채널 생성 (읽기, 쓰기모드)
		Path input = Paths.get("c:\\input.txt");
		try (FileChannel in = FileChannel.open(input, 
				StandardOpenOption.READ, StandardOpenOption.WRITE)) {
						
			// ByteBuffer 생성 (Direct)
			ByteBuffer buf = ByteBuffer.allocateDirect(100);
			Charset charset = Charset.defaultCharset();
			
			int count = 0;
			
			// 파일 끝날 때까지 파일 내용 읽기
			while(count >= 0) {
				
				count = in.read(buf);
				buf.flip();
				str += charset.decode(buf).toString();
				buf.clear();
			}
			
			// 파일 쓰기
			String str2 = "\nnew Hello world..!!";
			buf = charset.encode(str2);
			count = in.write(buf);
			
		} catch (Exception e) {

			System.out.println("예외 발생");
		}
		
		System.out.println(str);
	}
}
728x90

댓글

💲 추천 글