자바 의 io 모델 상세 설명

12215 단어 Javaio모형.
1. BIO
우 리 는 먼저 자바 예 를 보 자.

package cn.bridgeli.demo;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
 
/**
 * @author bridgeli
 */
public class SocketBIO {
 
 
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(9090, 20);
 
        System.out.println("step1: new ServerSocket(9090) ");
 
        while (true) {
            Socket client = server.accept();
            System.out.println("step2:client: " + client.getPort());
 
            new Thread(new Runnable() {
                @Override
                public void run() {
                    InputStream inputStream = null;
                    BufferedReader reader = null;
                    try {
                        inputStream = client.getInputStream();
                        reader = new BufferedReader(new InputStreamReader(inputStream));
                        while (true) {
                            String dataLine = reader.readLine(); //  2
                            if (null != dataLine) {
                                System.out.println(dataLine);
                            } else {
                                client.close();
                                break;
                            }
                        }
                        System.out.println("     ");
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (null != reader) {
                            try {
                                reader.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (null!= inputStream) {
                            try {
                                inputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
 
                }
 
 
            }).start();
 
        }
    }
 
}
BIO 는 최초의 IO 모델 로 이 모델 은 두 가지 큰 문제 가 있 습 니 다.1.accept 는 막 힌 것 입 니 다.2.read 도 차단 되 어 있 습 니 다.즉,우리 서버 가 일어나 면 accept 에서 차단 되 고 클 라 이언 트 연결 을 기다 리 지만 한 클 라 이언 트 가 연결 되 었 을 때 우 리 는 클 라 이언 트 로부터 데 이 터 를 읽 을 수 있 습 니 다.이때 도 차단 되 기 때문에 우리 의 시스템 은 단일 연결 일 수 밖 에 없습니다.여러 개의 클 라 이언 트 가 연결 되 었 을 때한 개 씩 줄 을 서서 연결 한 다음 에 클 라 이언 트 에서 데 이 터 를 읽 습 니 다.다 중 연결 을 실현 하기 위해 서 는 스 레 드 를 사용 하여 해결 해 야 합 니 다.처음에 클 라 이언 트 연결 을 기다 린 다음 에 클 라 이언 트 가 연 결 된 후에 스 레 드 를 시작 하여 클 라 이언 트 의 데 이 터 를 읽 은 다음 에 메 인 스 레 드 는 클 라 이언 트 연결 을 계속 기 다 려 야 합 니 다.
이 모델 의 가장 큰 문 제 는 바로 탄력 적 인 신축 능력 이 부족 하 다 는 것 이다.클 라 이언 트 의 동시 방 문 량 이 증가 한 후에 서버 의 스 레 드 갯 수 와 클 라 이언 트 의 동시 방 문 량 은 1:1 의 정비례 관 계 를 나타 내 고 자바 중의 스 레 드 도 비교적 소중 한 시스템 자원 이다.스 레 드 수량 이 신속하게 팽창 한 후에 시스템 의 성능 이 급 격 히 떨 어 질 것 이다.방 문 량 이 계속 증가 하면 서 시스템 은 결국 죽 었 다.물론 자바 뿐만 아니 라 우 리 는 만 개의 클 라 이언 트 가 서버 에 연결 되 고 서버 가 만 개의 스 레 드 를 켜 야 한다 고 가정 합 니 다.그러면 이 럴 때 서버 가 스 레 드 만 켜 면 얼마나 많은 자원 을 차지 해 야 합 니까?메모리 가 얼마나 필요 합 니까?운영 체제 가 이 스 레 드 를 조정 하기 위해 CPU 도 다 점용 되 어야 합 니까?
이 문 제 를 해결 하기 위해 누군가가 서버 의 스 레 드 모델 을 최적화 시 키 고 서버 는 스 레 드 풀 로 여러 개의 클 라 이언 트 요청 을 처리 합 니 다.하지만 문제 가 있 습 니 다.
1.스 레 드 총수 가 제한 되 어 있 고 기 다 려 야 합 니 다.
2.남 은 연결 은 작업 대기 열 에 쌓 여 있 습 니 다.작업 대기 열 이 가득 찼 을 때 거부 정책 을 사용 하기 시 작 했 기 때문에 근본적으로 문 제 를 해결 하지 못 했 습 니 다.
2. NIO
BIO 의 가장 큰 문 제 는 B,block,차단 입 니 다.그래서 이 문 제 를 해결 하면 됩 니 다.이때 NIO 가 생 겨 났 습 니 다.N 은 non-block 이라는 뜻 입 니 다.

package cn.bridgeli.demo;
 
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
 
/**
 * @author bridgeli
 */
public class SocketNIO {
 
    public static void main(String[] args) throws Exception {
 
        LinkedList<SocketChannel> clients = new LinkedList<>();
 
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(9090));
        serverSocketChannel.configureBlocking(false);
 
        while (true) {
            SocketChannel client = serverSocketChannel.accept();
 
            if (null != client) {
                client.configureBlocking(false);
                System.out.println("client port: " + client.socket().getPort());
                clients.add(client);
            }
 
            ByteBuffer buffer = ByteBuffer.allocateDirect(4096);
 
            for (SocketChannel c : clients) {
                int num = c.read(buffer);
                if (num > 0) {
                    buffer.flip();
                    byte[] aaa = new byte[buffer.limit()];
                    buffer.get(aaa);
 
                    String b = new String(aaa);
                    System.out.println(c.socket().getPort() + " : " + b);
                    buffer.clear();
                }
            }
        }
    }
 
}
이 럴 때 우 리 는 연결 과 읽 기 가 모두 막 히 지 않 는 다 는 것 을 알 게 될 것 이다.모두 막 히 지 않 기 때문에 모든 연결 을 저장 하고 연결 에서 데 이 터 를 읽 기 위해 집합 이 필요 하 다.이 모델 은 우리 가 스 레 드 를 열 어야 하 는 문 제 를 해결 하고 한 번 도 순환 하지 않 았 습 니 다.만약 에 새로운 연결 이 오 면 우 리 는 연결 을 집합 에 넣 고 연결 중의 데 이 터 를 하나씩 읽 습 니 다.이 때 는 우리 가 모든 스 레 드 를 연결 할 필요 가 없습니다.그러나 문제 가 있 습 니 다.연결 이 증가 함 에 따라 우리 의 대기 열 은 점점 커 질 것 입 니 다.그리고 우 리 는 매번 모든 연결 을 옮 겨 다 니 며 데 이 터 를 읽 어야 합 니 다.우 리 는 만 개의 연결 이 있다 고 가정 합 니 다.그러나 앞의 9999 개의 연결 은 데이터 가 없고 마지막 연결 만 데이터 가 있 습 니 다.그 전에 9999 번 읽 는 것 은 낭비 입 니 다.
3.다 중 재 활용
NIO 에서 읽 을 수 없 는 문 제 를 해결 하기 위해 서 이 때 우 리 는 사건 감청 에 따라 운영 체제 에 우리 가 그 사건 을 감청 한 다음 에 이 사건 들 이 데이터 가 도 착 했 을 때 우리 에 게 읽 으 라 고 알려 줄 수 있 습 니 다.예 는 다음 과 같 습 니 다.

package cn.bridgeli.demo;
 
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
 
/**
 * @author bridgeli
 */
public class SocketMultiplexingIO {
 
    private ServerSocketChannel serverSocketChannel = null;
    private Selector selector = null;
 
    public void initServer() {
        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(9090));
            selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void start() {
        initServer();
        System.out.println("      ...");
        try {
            while (true) {
 
                while (selector.select() > 0) {
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();
                        if (key.isAcceptable()) {
                            acceptHandler(key);
                        } else if (key.isReadable()) {
                            readHandler(key);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void acceptHandler(SelectionKey key) {
        try {
            ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
            SocketChannel client = ssc.accept();
            client.configureBlocking(false);
 
            ByteBuffer buffer = ByteBuffer.allocate(8192);
 
            client.register(selector, SelectionKey.OP_READ, buffer);
            System.out.println("    :" + client.getRemoteAddress());
 
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void readHandler(SelectionKey key) {
        SocketChannel client = (SocketChannel) key.channel();
        ByteBuffer buffer = (ByteBuffer) key.attachment();
        buffer.clear();
        int read = 0;
        try {
            while (true) {
                read = client.read(buffer);
                if (read > 0) {
                    buffer.flip();
                    while (buffer.hasRemaining()) {
                        client.write(buffer);
                    }
                    buffer.clear();
                } else if (read == 0) {
                    break;
                } else {
                    client.close();
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
 
        }
    }
 
    public static void main(String[] args) {
        SocketMultiplexingIO service = new SocketMultiplexingIO();
        service.start();
    }
}
다 중 재 활용 에 있어 서 poll,epoll,Selector 등 실현 방식 이 있 습 니 다.그 중에서 그들의 차 이 는 poll 은 우리 가 운영 체제 에 매번 어떤 사건 에 관심 을 가 져 야 하 는 지 알려 야 한 다 는 것 입 니 다.epoll 은 운영 체제 가 메모리 구역 을 열 어 우리 가 주목 해 야 할 일 을 저장 하고 매번 운영 체제 에 우리 가 어떤 사건 에 관심 을 가 지 는 지 알려 주지 않 아 도 됩 니 다.
BIO,NIO,다 중 재 활용 에 대해 말 병사 교육 의 주지 레이 선생님 은 매우 형상 적 인 예 가 있다.BIO 는 막 혔 기 때문에 우리 가 모든 라인 을 연결 해 야 한다.이것 은 우리 가 모든 차 를 위해 톨 게 이 트 에서 길 을 건설 하 는 것 과 같다.모든 차 는 길 을 수리 해 야 한다.우 리 는 스스로 차 에서 짐 을 내 려 야 한다.NIO 가 막 히 지 않 으 면 우 리 는 매번 톨 게 이 트 에 달 려 가서 우리 가 수리 한 도로 위 에 차 가 왔 는 지,오지 않 았 는 지 를 봐 야 한다.다음 에 보고 오 면 우 리 는 화물 을 내리 고 다음 에 새로운 물건 이 있 는 지 없 는 지 를 기 다 려 야 한다.다 중 재 활용 중인 poll 은 바로 우리 가 톨 게 이 트 에 전화 기 를 설치 한 다음 에 우리 가 전 화 를 할 때마다 내 가 관심 을 가 지 는 어떤 길이 차 가 왔 는 지,내 가 짐 을 내 려 야 하 는 지 하 는 것 이다.epoll 은 우리 가 톨 게 이 트 에 전화 기 를 설치 할 뿐만 아니 라 우 리 는 공책 도 남 겼 다.우 리 는 전 화 를 할 때마다 우리 가 새로 관심 을 가 지 는 길 을 톨 게 이 트 에 알려 준다.톨 게 이 트 는 공책 에 우리 가 주목 하 는 그 길 들 을 적어 놓 았 다.만약 에 우리 가 만 개의 길 을 주목한다 고 가정 하면 우 리 는 매번 전화 로 이 만 개의 길 을 한쪽 으로 말 할 필요 가 없다.이 길 들 에 차 가 왔 는 지,우리 가 짐 을 내 려 야 하 는 지 물 어 볼 필요 가 없다.
마지막 으로 몇 가지 작은 문 제 를 말씀 드 리 겠 습 니 다.
1.우 리 는 IO 모델 을 배 웁 니 다.IO 모델 은 운영 체제 가 우리 에 게 제공 하 는 인터페이스 로 시스템 호출 에 속 하기 때문에 우 리 는 strace 를 통 해 모든 프로그램 이 실행 하 는 시스템 호출 을 추적 할 수 있 습 니 다.명령 은 다음 과 같 습 니 다.

strace -ff -o out +       
2.우리 가 BIO 를 추적 할 때 JDK 의 최적화 로 인해 높 은 버 전의 JDK 를 사용 해도 차단 이 보이 지 않 습 니 다.이때 JDK 1.4 컴 파일 을 통 해 실행 할 수 있 습 니 다.(이것 도 우리 가 lambda 표현 식 과 try-with-resource 를 사용 하 는 이유 입 니 다)
3.IO 호출 은 시스템 호출 에 속 하기 때문에 BIO->NIO->다 중 재 활용 은 운영 체제 의 발전 이다.그러나 우 리 는 각종 언어 로 쓰 인 것 이 응용 에 속 하기 때문에 비동기 비 차단 IO 모델 이 있 는 지 없 는 지 를 보면 운영 체제 의 밑바닥 에 이런 모델 이 있 는 지 없 는 지 를 볼 수 있다.운영 체제 가 우리 에 게 비동기 비 차단 IO 와 관련 된 인 터 페 이 스 를 제공 해 야 한다.우리 의 응용 만 이 한층 더 최적화 할 수 있다.
4.우 리 는 strace 를 통 해 추 적 된 모든 시스템 호출 을 통 해 man 명령 을 통 해 문 서 를 볼 수 있 습 니 다(Liux 시스템,비 Windows 시스템).man 명령 이 없 으 면 설치 하면 됩 니 다.
이상 은 자바 중의 io 모델 에 대한 상세 한 내용 입 니 다.자바 io 모델 에 관 한 자 료 는 우리 의 다른 관련 글 을 주목 하 세 요!

좋은 웹페이지 즐겨찾기