자바 I/O 모델 의 발전 탐색
동기 화 와 비동기
사용자 스 레 드 와 커 널 의 상호작용 방식 을 설명 합 니 다.
4.567917.동기 화 는 사용자 스 레 드 가 I/O 요청 을 한 후에 커 널 I/O 작업 이 완 료 된 후에 야 계속 실행 할 수 있다 는 것 을 말한다4.567917.비동기 란 사용자 스 레 드 가 I/O 요청 을 한 후에 도 계속 실행 하 는 것 을 말 합 니 다.커 널 I/O 작업 이 끝 난 후에 사용자 스 레 드 를 알 리 거나 사용자 스 레 드 가 등 록 된 리 셋 함 수 를 호출 합 니 다차단 과 비 차단
사용자 스 레 드 가 커 널 I/O 를 호출 하 는 방식 을 설명 합 니 다.
4.567917.차단 이란 I/O 작업 이 철저히 완 성 된 후에 야 사용자 공간 으로 돌아 가 는 것 을 말한다4.567917.비 차단 이란 I/O 작업 이 호출 된 후에 바로 사용자 에 게 상태 값 을 되 돌려 주 는 것 을 말 하 며 I/O 작업 이 완 료 될 때 까지 기다 릴 필요 가 없다하나의 I/O 작업 은 사실 두 단계 로 나 뉘 었 다.I/O 요청 과 실제 I/O 작업 을 시작 하 는 것 이다.차단 I/O 와 비 차단 I/O 의 차 이 는 첫 번 째 단계 에 있 습 니 다.I/O 요청 이 막 힐 수 있 는 지,완 료 될 때 까지 막 히 면 전통 적 인 차단 I/O 입 니 다.막 히 지 않 으 면 비 차단 I/O 입 니 다.동기 화 I/O 와 비동기 I/O 의 차 이 는 두 번 째 단계 가 막 히 는 지 여부 에 있 습 니 다.실제 I/O 읽 기와 쓰기 가 막 히 면 동기 화 I/O 입 니 다.
유 닉 스 I/O 모델
유 닉 스 아래 에는 모두 다섯 가지 I/O 모델 이 있다.
차단 I/O비 차단 I/OI/O 복용(select 와 poll)신호 구동 I/O(SIGIO)비동기 I/O(POSIX 의 ao계열 함수)차단 I/O
요청 이 즉시 완료 되 지 않 으 면 차단 을 유지 합 니 다.
단계 1:데이터 가 준비 되 기 를 기 다 립 니 다.네트워크 I/O 의 상황 은 원 격 데이터 가 속속 도착 하 기 를 기다 리 는 것 이다.디스크 I/O 의 경우 디스크 데이터 가 디스크 에서 커 널 메모리 로 읽 히 기 를 기다 리 는 것 입 니 다.
단계 2:데 이 터 를 커 널 에서 프로 세 스 로 복사 합 니 다.시스템 보안 때문에 사용자 상태의 프로그램 은 커 널 메모리 를 직접 읽 을 수 있 는 권한 이 없 기 때문에 커 널 은 커 널 메모리 의 데 이 터 를 사용자 상태 메모리 에 복사 하 는 것 을 책임 집 니 다.
비 차단 I/O
4.567917.데 이 터 는 커 널 에서 사용자 공간 으로 복사 할 준비 가 되 어 있 습 니 다
일반적으로 이런 모델 을 직접 사용 하지 않 고 다른 I/O 모델 에서 비 차단 I/O 라 는 특성 을 사용한다.이런 방식 은 단일 I/O 요청 에 큰 의미 가 없 지만 I/O 다 중 재 활용 에 도 로 를 평평 하 게 깔 았 다.
I/O 재 활용(비동기 차단 I/O)
I/O 다 중 재 활용 은 select 나 poll 함 수 를 사용 합 니 다.이 두 함 수 는 프로 세 스 를 막 을 수 있 지만 I/O 를 막 는 것 과 달리 이 두 함 수 는 여러 개의 I/O 작업 을 동시에 막 을 수 있 습 니 다.또한 여러 개의 읽 기 동작,여러 개의 쓰기 작업 의 I/O 함 수 를 동시에 검사 할 수 있 으 며,데이터 가 읽 을 수 있 거나 쓸 수 있 을 때 까지 I/O 작업 함 수 를 진정 으로 호출 할 수 있 습 니 다.
프로 세 스 를 보면 select 함 수 를 사용 하여 I/O 요청 을 하 는 것 과 동기 화 블록 모델 은 큰 차이 가 없 으 며 감시 socket 을 추가 하고 select 함 수 를 호출 하 는 추가 작업 도 많아 효율 이 더욱 떨어진다.그러나 select 를 사용 한 후 가장 큰 장점 은 사용자 가 한 라인 에서 여러 socket 의 I/O 요청 을 동시에 처리 할 수 있다 는 것 이다.사용 자 는 여러 개의 socket 을 등록 한 후에 select 를 계속 호출 하여 활성 화 된 socket 을 읽 으 면 같은 스 레 드 에서 여러 개의 I/O 요청 을 동시에 처리 하 는 목적 을 달성 할 수 있 습 니 다.동기 화 블록 모델 에 서 는 다 중 스 레 드 방식 을 통 해 이 목적 을 달성 해 야 한다.
I/O 다 중 복용 모델 은 Reactor 디자인 모델 을 사용 하여 이 메커니즘 을 실현 했다.
select/poll 을 호출 하 는 방법 은 한 사용자 의 상태 스 레 드 가 여러 socket 을 돌아 가면 서 특정한 단계 1 의 데이터 가 완 료 될 때 까지 실제 사용자 스 레 드 실행 단계 2 의 복사 본 을 알려 줍 니 다.전문 적 인 사용자 상태 스 레 드 를 통 해 비 차단 I/O 폴 링 을 실행 하고 단계 1 의 비동기 화 를 모 의 했다.
신호 구동 I/O(SIGIO)
우선 socket 에서 신호 구동 I/O 를 허용 하고 신호 처리 함 수 를 설치 합 니 다.프로 세 스 가 계속 실행 되 는 것 은 막 히 지 않 습 니 다.데이터 가 준비 되 었 을 때 프로 세 스 는 SIGIO 신 호 를 받 고 신호 처리 함수 에서 I/O 조작 함수 로 데 이 터 를 처리 할 수 있 습 니 다.
비동기 I/O
aio 호출read 함수,커 널 설명 글자,버퍼 포인터,버퍼 크기,파일 오프셋 및 알림 방식 을 알려 주 고 즉시 돌아 갑 니 다.커 널 이 데 이 터 를 버퍼 에 복사 한 후에 프로그램 에 알 립 니 다.
비동기 I/O 모델 은 Proactor 디자인 모델 을 사용 하여 이 메커니즘 을 실현 했다.
커 널 에 전체 과정(단계 1 과 단계 2 포함)이 모두 완 료 될 때 프로그램 에 데 이 터 를 읽 으 라 고 알려 줍 니 다.
몇 가지 I/O 모델 의 비교
앞의 네 가지 모델 의 차 이 는 단계 1 이 다 르 고 단계 2 가 대체적으로 같 으 며 모두 데 이 터 를 커 널 에서 호출 자의 버퍼 로 복사 하 는 것 이다.비동기 I/O 의 두 단 계 는 모두 앞의 네 모델 과 다르다.
동기 화 I/O 작업 은 I/O 작업 이 끝 날 때 까지 요청 프로 세 스 를 막 습 니 다.비동기 I/O 작업 은 요청 프로 세 스 를 막 지 않 습 니 다.
흔 한 자바 I/O 모델
유 닉 스 의 I/O 모델 을 알 아 본 뒤 자바 의 I/O 모델 도 비슷 하 다.
"I/O 차단"모드
이전 소켓 섹 션 에 있 는 EchoServer 는 간단 한 I/O 차단 예 입 니 다.서버 가 시 작 된 후에 클 라 이언 트 연결 을 기다 리 고 있 습 니 다.클 라 이언 트 가 서버 에 연결 되면 서버 는 읽 기와 쓰기 데이터 흐름 을 막는다.
EchoServer 코드:
public class EchoServer {
public static int DEFAULT_PORT = 7;
public static void main(String[] args) throws IOException {
int port;
try {
port = Integer.parseInt(args[0]);
} catch (RuntimeException ex) {
port = DEFAULT_PORT;
}
try (
ServerSocket serverSocket =
new ServerSocket(port);
Socket clientSocket = serverSocket.accept();
PrintWriter out =
new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(clientSocket.getInputStream()));
) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
out.println(inputLine);
}
} catch (IOException e) {
System.out.println("Exception caught when trying to listen on port "
+ port + " or listening for a connection");
System.out.println(e.getMessage());
}
}
}
"I/O+다 중 스 레 드 차단"모드 로 개선다 중 스 레 드 를 사용 하여 여러 클 라 이언 트 가 서버 에 접근 하 는 것 을 지원 합 니 다.
메 인 스 레 드 MultiThreadEchoServer.java
public class MultiThreadEchoServer {
public static int DEFAULT_PORT = 7;
public static void main(String[] args) throws IOException {
int port;
try {
port = Integer.parseInt(args[0]);
} catch (RuntimeException ex) {
port = DEFAULT_PORT;
}
Socket clientSocket = null;
try (ServerSocket serverSocket = new ServerSocket(port);) {
while (true) {
clientSocket = serverSocket.accept();
// MultiThread
new Thread(new EchoServerHandler(clientSocket)).start();
}
} catch (IOException e) {
System.out.println(
"Exception caught when trying to listen on port " + port + " or listening for a connection");
System.out.println(e.getMessage());
}
}
}
프로세서 클래스 EchoServerHandler.java
public class EchoServerHandler implements Runnable {
private Socket clientSocket;
public EchoServerHandler(Socket clientSocket) {
this.clientSocket = clientSocket;
}
@Override
public void run() {
try (PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));) {
String inputLine;
while ((inputLine = in.readLine()) != null) {
out.println(inputLine);
}
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
문제 가 있 습 니 다:새로운 연결 을 받 을 때마다 스 레 드 를 새로 만들어 야 합 니 다.처리 가 끝 난 후에 스 레 드 를 없 애 는 대가 가 큽 니 다.대량의 짧 은 연결 이 나타 날 때 성능 이 비교적 낮다."I/O+스 레 드 탱크 차단"모드 로 개선
위의 다 중 스 레 드 모델 에 나타 난 스 레 드 중복 생 성,소각 에 따 른 비용 은 스 레 드 풀 로 최적화 할 수 있 습 니 다.매번 새로운 연결 을 받 은 후에 풀 에서 빈 스 레 드 를 가 져 와 처리 하고 처리 가 끝 난 후에 다시 풀 에 넣 으 며 다시 스 레 드 를 사용 하면 주파수 적 으로 스 레 드 를 만 들 고 없 애 는 비용 을 피 할 수 있 습 니 다.
주 스 레 드 ThreadPoolEchoServer.java
public class ThreadPoolEchoServer {
public static int DEFAULT_PORT = 7;
public static void main(String[] args) throws IOException {
int port;
try {
port = Integer.parseInt(args[0]);
} catch (RuntimeException ex) {
port = DEFAULT_PORT;
}
ExecutorService threadPool = Executors.newFixedThreadPool(5);
Socket clientSocket = null;
try (ServerSocket serverSocket = new ServerSocket(port);) {
while (true) {
clientSocket = serverSocket.accept();
// Thread Pool
threadPool.submit(new Thread(new EchoServerHandler(clientSocket)));
}
} catch (IOException e) {
System.out.println(
"Exception caught when trying to listen on port " + port + " or listening for a connection");
System.out.println(e.getMessage());
}
}
}
문제 가 존재 합 니 다.대량의 짧 은 연결 장면 에서 성능 이 향상 되 었 습 니 다.매번 스 레 드 를 만 들 거나 없 애 지 않 고 연결 탱크 의 스 레 드 를 다시 사용 하기 때 문 입 니 다.그러나 대량의 긴 연결 장면 에서 스 레 드 가 연결 되 어 장기 적 으로 점용 되 기 때문에 스 레 드 를 자주 만 들 고 없 앨 필요 가 없 기 때문에 장점 이 없다.이런 방법 은 중간 규모 의 클 라 이언 트 의 병발 수 에 적용 할 수 있 지만 연결 수가 100,000 이상 이면 성능 이 좋 지 않 을 것 이다.
'비 차단 I/O'모드 로 개선
'차단 I/O+스 레 드 탱크'네트워크 모델 은'차단 I/O+다 중 스 레 드'네트워크 모델 보다 성능 이 향상 되 었 지만 이 두 가지 모델 은 모두 공 통 된 문제 가 존재 한다.읽 기와 쓰기 작업 은 모두 동기 적 으로 차단 되 고 큰 병행(대량의 연결 을 지속 하 는 동시에 요청)장면 에 직면 하여 대량의 스 레 드 를 소모 하여 연결 을 유지 해 야 한다.CPU 는 대량의 라인 사이 에서 빈번하게 전환 되 어 성능 손실 이 매우 크다.단일 기기 의 연결 이 1 만 명 을 넘 고 수만 명 에 이 르 면 서버 의 성능 이 급 격 히 떨어진다.
그러나 NIO 의 Selector 는 이 문 제 를 잘 해결 했다.메 인 스 레 드(하나의 스 레 드 또는 CPU 개수 의 스 레 드)로 모든 연결 을 유지 하고 클 라 이언 트 가 연 결 된 데 이 터 를 관리 하고 읽 으 며 읽 은 데 이 터 를 뒤의 스 레 드 풀 에 맡 겼 다.스 레 드 풀 은 업무 논 리 를 처리 한 후에 결 과 를 메 인 스 레 드 에 응답 을 보 냈 다.소량의 스 레 드 로 대량의 연결 요청 을 처리 할 수 있 습 니 다.
자바 NIO 는 다음 과 같은 몇 가지 핵심 부분 으로 구성 되 어 있 습 니 다.
주 스 레 드 NonBloking EchoServer.java
public class NonBlokingEchoServer {
public static int DEFAULT_PORT = 7;
public static void main(String[] args) throws IOException {
int port;
try {
port = Integer.parseInt(args[0]);
} catch (RuntimeException ex) {
port = DEFAULT_PORT;
}
System.out.println("Listening for connections on port " + port);
ServerSocketChannel serverChannel;
Selector selector;
try {
serverChannel = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(port);
serverChannel.bind(address);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException ex) {
ex.printStackTrace();
return;
}
while (true) {
try {
selector.select();
} catch (IOException ex) {
ex.printStackTrace();
break;
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
SelectionKey clientKey = client.register(selector,
SelectionKey.OP_WRITE | SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(100);
clientKey.attach(buffer);
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
client.read(output);
}
if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
output.flip();
client.write(output);
output.compact();
}
} catch (IOException ex) {
key.cancel();
try {
key.channel().close();
} catch (IOException cex) {
}
}
}
}
}
}
'비동기 I/O'모드 로 개선Java SE 7 버 전 이후 비동기 I/O(NIO.2)지원 을 도입 해 고성능 네트워크 애플 리 케 이 션 구축 에 유리 한 장 치 를 제공 했다.
메 인 스 레 드 AsyncEchoServer.java
public class AsyncEchoServer {
public static int DEFAULT_PORT = 7;
public static void main(String[] args) throws IOException {
int port;
try {
port = Integer.parseInt(args[0]);
} catch (RuntimeException ex) {
port = DEFAULT_PORT;
}
ExecutorService taskExecutor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
// create asynchronous server socket channel bound to the default group
try (AsynchronousServerSocketChannel asynchronousServerSocketChannel = AsynchronousServerSocketChannel.open()) {
if (asynchronousServerSocketChannel.isOpen()) {
// set some options
asynchronousServerSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 4 * 1024);
asynchronousServerSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
// bind the server socket channel to local address
asynchronousServerSocketChannel.bind(new InetSocketAddress(port));
// display a waiting message while ... waiting clients
System.out.println("Waiting for connections ...");
while (true) {
Future<AsynchronousSocketChannel> asynchronousSocketChannelFuture = asynchronousServerSocketChannel
.accept();
try {
final AsynchronousSocketChannel asynchronousSocketChannel = asynchronousSocketChannelFuture
.get();
Callable<String> worker = new Callable<String>() {
@Override
public String call() throws Exception {
String host = asynchronousSocketChannel.getRemoteAddress().toString();
System.out.println("Incoming connection from: " + host);
final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
// transmitting data
while (asynchronousSocketChannel.read(buffer).get() != -1) {
buffer.flip();
asynchronousSocketChannel.write(buffer).get();
if (buffer.hasRemaining()) {
buffer.compact();
} else {
buffer.clear();
}
}
asynchronousSocketChannel.close();
System.out.println(host + " was successfully served!");
return host;
}
};
taskExecutor.submit(worker);
} catch (InterruptedException | ExecutionException ex) {
System.err.println(ex);
System.err.println("
Server is shutting down ...");
// this will make the executor accept no new threads
// and finish all existing threads in the queue
taskExecutor.shutdown();
// wait until all threads are finished
while (!taskExecutor.isTerminated()) {
}
break;
}
}
} else {
System.out.println("The asynchronous server-socket channel cannot be opened!");
}
} catch (IOException ex) {
System.err.println(ex);
}
}
}
소스 코드이 장 에서 예 를 들 어 소스 코드 는https://github.com/waylau/essential-java중 com.waylau.essentialjava.net.echo 가방 에서 찾 을 수 있 습 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Is Eclipse IDE dying?In 2014 the Eclipse IDE is the leading development environment for Java with a market share of approximately 65%. but ac...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.