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