Java中BIO和NIO区别


前言

  自己一直没有去了解IO方面的知识,对于IO(包括Socket编程)都很模糊,通过一段时间的学习,有所了解并记录如下。

  github代码地址:https://github.com/Chenrencun/io-learn

正文

  一、BIO(同步阻塞IO)

  1、同步阻塞IO,服务器端的实现模式是一个连接建立一个线程。也就是说,当客户端有连接请求时,服务器端会启动一个线程去处理。假如这个连接不做任何事,就会导致一些不必要的开销,可以通过线程池改善。

  2、实现:

  (1)单线程:

   ①服务器端:

    public static void server(){
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;char[] chars = new char[1024];
        try {
            serverSocket = new ServerSocket(8888);
            System.out.println("**********服务器即将启动,等待客户端的连接*************");
            while (true){
                socket = serverSocket.accept();

                // 连接建立好之后,从socket中获取输入流
                inputStream = socket.getInputStream();
                inputStreamReader = new InputStreamReader(inputStream);
                inputStreamReader.read(chars);
                System.out.println("客户端说:" + new String(chars, 0 ,chars.length));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStreamReader.close();
                inputStream.close();
                socket.close();
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

   ②客户端:

    public static void client(){
        Socket socket = null;
        OutputStream outputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        try {
            socket = new Socket("localhost", 8888);
            outputStream = socket.getOutputStream();
            outputStreamWriter = new OutputStreamWriter(outputStream);
            outputStreamWriter.write("This is Bio!");
            outputStreamWriter.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStreamWriter.close();
                outputStream.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 

  (2)多线程:

   ①服务端:

    public static void server(){
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(8888);
            System.out.println("**********服务器即将启动,等待客户端的连接*************");
            while (true){
                socket = serverSocket.accept();

                // 连接建立好之后,每个连接新建一个线程去处理
                new Thread(new MyRunnable(socket)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    // 内部类(处理客户端请求)
    static class MyRunnable implements Runnable{

        private Socket socket;

        public MyRunnable(Socket socket){
            this.socket=socket;
        }

        @Override
        public void run() {
            InputStream inputStream = null;
            InputStreamReader inputStreamReader = null;
            char[] chars = new char[1024];
            try {
                inputStream = socket.getInputStream();
                inputStreamReader = new InputStreamReader(inputStream);
                inputStreamReader.read(chars);
                System.out.println("客户端说:" + new String(chars, 0 ,chars.length));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStreamReader.close();
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

   ②客户端:

    public static void client(){
        Socket socket = null;
        OutputStream outputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        try {
            socket = new Socket("localhost", 8888);
            outputStream = socket.getOutputStream();
            outputStreamWriter = new OutputStreamWriter(outputStream);
            outputStreamWriter.write("This is Bio!");
            outputStreamWriter.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStreamWriter.close();
                outputStream.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 

  (3)线程池:

   ①服务端:

    public static void server(){
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        char[] chars = new char[1024];
        try {
            serverSocket = new ServerSocket(8888);
            System.out.println("**********服务器即将启动,等待客户端的连接*************");
            while (true){
                socket = serverSocket.accept();

                // 通过线程池机制去处理
                executorService.submit(new MyRunnable(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStreamReader.close();
                inputStream.close();
                socket.close();
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    // 内部类(处理客户端请求)
    static class MyRunnable implements Runnable{

        private Socket socket;

        public MyRunnable(Socket socket){
            this.socket=socket;
        }

        @Override
        public void run() {
            InputStream inputStream = null;
            InputStreamReader inputStreamReader = null;
            char[] chars = new char[1024];
            try {
                inputStream = socket.getInputStream();
                inputStreamReader = new InputStreamReader(inputStream);
                inputStreamReader.read(chars);
                System.out.println("客户端说:" + new String(chars, 0 ,chars.length));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStreamReader.close();
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

   ②客户端:

    public static void client(){
        Socket socket = null;
        OutputStream outputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        try {
            socket = new Socket("localhost", 8888);
            outputStream = socket.getOutputStream();
            outputStreamWriter = new OutputStreamWriter(outputStream);
            outputStreamWriter.write("This is Bio!");
            outputStreamWriter.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                outputStreamWriter.close();
                outputStream.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 

  二、NIO(同步非阻塞IO)

  1、同步非阻塞IO,服务器的实现模式是一个IO请求建立一个线程。也就是说,客户端的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时,会启动一个线程去处理。

  2、实现:

  (1)单线程:

    ①服务端:

    public static void server(){
        ServerSocketChannel serverSocketChannel = null;
        Selector selector = null;
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8888));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                // select方法指定超时时间,非阻塞
                if (selector.select(2000) == 0) {
                    System.out.println("*");
                }

                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    //如果服务端信道感兴趣的I/O操作为accept
                    if (selectionKey.isAcceptable()){
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    //如果客户端信道感兴趣的I/O操作为read
                    if (selectionKey.isReadable()){
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int read = socketChannel.read(buffer);
                        if (read == -1){
                            // 客户端关闭
                            selectionKey.cancel();
                        } else {
                            // 对客户端信息处理
                            System.out.println("客户端说:" + new String(buffer.array()));
                        }
                    }
                    
                    //这里需要手动从键集中移除当前的key
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                selector.close();
                serverSocketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

   ②客户端:

    public static void client(){
        SocketChannel socketChannel = null;
        Selector selector = null;
        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("localhost", 8888));
            socketChannel.register(selector, SelectionKey.OP_READ);
            while (!socketChannel.finishConnect()){
                System.out.println("正在连接中");
            }
            System.out.println("成功连接到服务端");
            // 向服务端发送信息
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.put("This is Nio!".getBytes());
            buffer.flip();
            socketChannel.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                selector.close();
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 

  (2)多线程:

   ①服务端:

    public static void server(){
        ServerSocketChannel serverSocketChannel = null;
        Selector selector = null;
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8888));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                // select方法指定超时时间,非阻塞
                if (selector.select(2000) == 0) {
                    System.out.println("*");
                }

                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    //如果服务端信道感兴趣的I/O操作为accept
                    if (selectionKey.isAcceptable()){
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    //如果客户端信道感兴趣的I/O操作为read
                    if (selectionKey.isReadable()){
                        // 针对每次read,新建一个线程去处理
                        new Thread(new MyRunnable(selectionKey)).start();
                    }
                    //这里需要手动从键集中移除当前的key
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                selector.close();
                serverSocketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    static class MyRunnable implements Runnable{

        private SelectionKey selectionKey;

        public MyRunnable(SelectionKey selectionKey){
            this.selectionKey = selectionKey;
        }

        @Override
        public void run() {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int read = 0;
            try {
                read = socketChannel.read(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (read == -1){
                // 客户端关闭
                selectionKey.cancel();
            } else {
                // 对客户端信息处理
                System.out.println("客户端说:" + new String(buffer.array()));
            }
        }
    }

   ②客户端:

    public static void client(){
        SocketChannel socketChannel = null;
        Selector selector = null;
        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("localhost", 8888));
            socketChannel.register(selector, SelectionKey.OP_READ);
            while (!socketChannel.finishConnect()){
                System.out.println("正在连接中");
            }
            System.out.println("成功连接到服务端");
            // 向服务端发送信息
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.put("This is Nio!".getBytes());
            buffer.flip();
            socketChannel.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                selector.close();
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 

  (3)线程池:

   ①服务端:

    private static ExecutorService executorService = new ThreadPoolExecutor(3, 5, 3,
            TimeUnit.MINUTES, new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void server(){
        ServerSocketChannel serverSocketChannel = null;
        Selector selector = null;
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8888));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true) {
                // select方法指定超时时间,非阻塞
                if (selector.select(2000) == 0) {
                    System.out.println("*");
                }

                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    //如果服务端信道感兴趣的I/O操作为accept
                    if (selectionKey.isAcceptable()){
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                    }
                    //如果客户端信道感兴趣的I/O操作为read
                    if (selectionKey.isReadable()){
                        // 针对每次read,使用线程池机制去处理
                        executorService.submit(new MyRunnable(selectionKey));
                    }
                    //这里需要手动从键集中移除当前的key
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                selector.close();
                serverSocketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    static class MyRunnable implements Runnable{

        private SelectionKey selectionKey;

        public MyRunnable(SelectionKey selectionKey){
            this.selectionKey = selectionKey;
        }

        @Override
        public void run() {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int read = 0;
            try {
                read = socketChannel.read(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (read == -1){
                // 客户端关闭
                selectionKey.cancel();
            } else {
                // 对客户端信息处理
                System.out.println("客户端说:" + new String(buffer.array()));
            }
        }
    }

   ②客户端:

    public static void client(){
        SocketChannel socketChannel = null;
        Selector selector = null;
        try {
            selector = Selector.open();
            socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("localhost", 8888));
            socketChannel.register(selector, SelectionKey.OP_READ);
            while (!socketChannel.finishConnect()){
                System.out.println("正在连接中");
            }
            System.out.println("成功连接到服务端");
            // 向服务端发送信息
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.put("This is Nio!".getBytes());
            buffer.flip();
            socketChannel.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                selector.close();
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM