zoukankan      html  css  js  c++  java
  • IO和NIO的对比篇

    当遇到并发服务场景时,我们可以采取如下措施:


    一、同步阻塞IO实现

    IO和NIO的对比篇

     

    public class DemoServer extends Thread {
        private ServerSocket serverSocket;
    
    
        public int getPort() {
            return serverSocket.getLocalPort();
        }
    
    
        public void run() {
            try {
                serverSocket = new ServerSocket(0);
                while (true) {
                    // 非常占用内存资源,每个客户端启用一个线程,十分不合理
                    Socket socket = serverSocket.accept();
                    RequesHandler requesHandler = new RequesHandler(socket);
                    requesHandler.start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (serverSocket != null) {
                    try {
                        serverSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    ;
                }
            }
        }
    
    
        public static void main(String[] args) throws IOException {
            DemoServer server = new DemoServer();
            server.start();
            try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) {
                BufferedReader buferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
                buferedReader.lines().forEach(s -> System.out.println(s));
            }
        }
    }
    
    
    // 简化实现,不做读取,直接发送字符串
    class RequesHandler extends Thread {
        private Socket socket;
    
    
        RequesHandler(Socket socket) {
            this.socket = socket;
        }
    
    
        @Override
        public void run() {
            try (PrintWriter out = new PrintWriter(socket.getOutputStream());) {
                out.println("Hello world!");
                out.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    每次 new 一个线程或者销毁一个线程是有明显的开销的,每个线程都有单独的线程结构,非常占用内存资源,每个客户端启用一个线程是十分不合理的, 因此可以采用线程池的方式进行优化。

    // 也是阻塞IO,采用线程池的方式处理请求,当来一个新的客户端连接时,
    // 将请求 Socket 封装成一个 task ,放到线程池中取执行。
    
    
    serverSocket = new ServerSocket(0);
    executor = Executors.newFixedThreadPool(8);
    while (true) {
        Socket socket = serverSocket.accept();
        RequesHandler requesHandler = new RequesHandler(socket);
        executor.execute(requesHandler);
    }

    二、NIO实现

     

    NIO(非阻塞IO) 多路复用机制

    IO和NIO的对比篇

     

    public class NIOServer extends Thread {
        public void run() {
            try (Selector selector = Selector.open(); ServerSocketChannel serverSocket = ServerSocketChannel.open();) {// 创建Selector和Channel
                serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888));
                serverSocket.configureBlocking(false);
                // 注册到Selector,并说明关注点
                serverSocket.register(selector, SelectionKey.OP_ACCEPT);
                while (true) {
                    selector.select();// 阻塞等待就绪的Channel,这是关键点之一
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iter = selectedKeys.iterator();
                    while (iter.hasNext()) {
                        SelectionKey key = iter.next();
                        // 生产系统中一般会额外进行就绪状态检查
                        sayHelloWorld((ServerSocketChannel) key.channel());
                        iter.remove();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
        private void sayHelloWorld(ServerSocketChannel server) throws IOException {
            try (SocketChannel client = server.accept();) {
                ByteBuffer readBuffer = ByteBuffer.allocate(32);
                client.read(readBuffer);
                System.out.println("Server received : " + new String(readBuffer.array()));
                ByteBuffer writeBuffer = ByteBuffer.allocate(128);
                writeBuffer.put("hello xiaoming".getBytes());
                writeBuffer.flip();
                client.write(writeBuffer);
                //client.write(Charset.defaultCharset().encode("Hello world!"));
            }
        }
    
    
        public static void main(String[] args) throws IOException {
            NIOServer server = new NIOServer();
            server.start();
            
            try {
                SocketChannel socketChannel = SocketChannel.open();
                socketChannel.connect(new InetSocketAddress(InetAddress.getLocalHost(), 8888));
    
    
                ByteBuffer writeBuffer = ByteBuffer.allocate(32);
                ByteBuffer readBuffer = ByteBuffer.allocate(32);
    
    
                writeBuffer.put("hello".getBytes());
                writeBuffer.flip();
    
    
                while (true) {
                    writeBuffer.rewind();
                    socketChannel.write(writeBuffer);
    //                readBuffer.clear();
                    socketChannel.read(readBuffer);
                    System.out.println("Client received : " + new String(readBuffer.array()));
                }
            } catch (IOException e) {
            }
            
        }
    
    
    /**
     * @return
     */
    private int getPort() {
        return 8888;
    }

    三、总结

    在前面两个例子中:阻塞IO和伪异步IO,一个是使用 new 线程的方式,另外一个是采用线程池管理的方式, IO都是同步阻塞模式,所以需要多线程以实现多任务处理。

    而 NIO 则是利用了单线程轮询事件的机制,通过高效地定位就绪的Channel,来决定做什么,仅仅select阶段是阻塞的,可以有效避免大量客户端连接时频繁线程切换带来的问题,应用的扩展能力有了非常大的提高。

  • 相关阅读:
    声明、定义本质的区别:有无内存的分配
    typedef 与 define 的区别
    Linux内核中链表的学习
    C语言数据类型的转换
    状态机
    170311php添加留言页面
    170314网络编程之TCP聊天窗口
    php课堂2简单作业+文件上传之案例
    php案例2——用户列表页
    学生管理系统
  • 原文地址:https://www.cnblogs.com/javadoudi/p/14284248.html
Copyright © 2011-2022 走看看