乐趣区

关于程序员:IO和NIO的对比篇

当遇到并发服务场景时,咱们能够采取如下措施:


一、同步阻塞 IO 实现

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)多路复用机制

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 阶段是阻塞的,能够无效防止大量客户端连贯时频繁线程切换带来的问题,利用的扩大能力有了十分大的进步。

退出移动版