码迷,mamicode.com
首页 > 其他好文 > 详细

浅谈NIO(四)

时间:2018-08-15 14:57:37      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:nbsp   close   cte   false   启动   ada   cat   事件   sch   

IO同步阻塞与同步非阻塞(IO为同步阻塞形式,NIO为同步非阻塞形式,NIO并没有实现异步,在JDK1.7后升级NIO库包,支持异步非阻塞(AIO),阻塞IO和非阻塞IO都是在网络编程的时候产生的,本地是没有这两个概念的)

IO(BIO)和NIO区别:

其本质就是阻塞和非阻塞的区别。

BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果服务端启动但是没有得到请求那么就会一直阻塞。比如去食堂吃饭,要排队等待。

NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上(selector),多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 服务器在没有得到到io请求时,也可以做自己的事情,是非阻塞的。比如去医院看病,挂完号就可以做别的事,直到广播喊到自己的号码。

AIO和NIO区别:

AIO是异步的,即所有的IO读写操作交给操作系统,我们不需要关心io读写,当操作系统完成了IO读写操作时,会给我们应用程序发送通知,我们的应用程序直接拿走数据极即可。比如有快递到了的时候,我们可以不用自己去拿,打个电话让别人取件然后送上门即可。

NIO是同步的,应用程序会直接参与IO读写操作,或者采用轮训的策略实时检查数据的就绪状态,如果就绪则获取数据。比如有快递到了的时候,我们自己去取件。

什么是伪异步BIO?

BIO模式时,服务器实现模式为一个连接一个线程,此时客户端发送请求时,服务端需要开启一个线程,如果客户端有1000个请求,那么服务端需要开启1000个线程,这样就超级消耗资源,所以需要在服务端利用线程池处理请求。但是此时依然没有解决阻塞问题。

//tcp服务器端(socket底层也是阻塞的)
class TcpServer {
     
    public static void main(String[] args) throws IOException {
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        System.out.println("socket tcp服务器端启动....");
        ServerSocket serverSocket = new ServerSocket(8080);
        // 等待客户端请求
        try {
            while (true) {
                Socket accept = serverSocket.accept();
                //使用线程
                newCachedThreadPool.execute(new Runnable() {
                   @Override
                    public void run() {
                        try {
                            InputStream inputStream = accept.getInputStream();
                            // 转换成string类型
                            byte[] buf = new byte[1024];
                            int len = inputStream.read(buf);
                            String str = new String(buf, 0, len);
                            System.out.println("服务器接受客户端内容:" + str);
                        } catch (Exception e) {
throw new RuntimeException(e);
} } }); } } catch (Exception e) { e.printStackTrace(); } finally { serverSocket.close(); } } }
//tcp客户端
public class TcpClient {
    public static void main(String[] args) throws UnknownHostException, IOException {
        System.out.println("socket tcp 客户端启动....");
        Socket socket = new Socket("127.0.0.1", 8080);
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("你好啊".getBytes());
        socket.close();
    }
}

IO之间的关系

技术分享图片

选择器:

1、SelectionKey.OP_CONNECT  连接

2、SelectionKey.OP_ACCEPT     接受

3、SelectionKey.OP_READ          可读

4、SelectionKey.OP_WRITE        可写

NIO非阻塞代码

// nio
class Server {
    public static void main(String[] args) throws IOException {
        System.out.println("服务器端已经启动....");
        // 1.创建通道
        ServerSocketChannel sChannel = ServerSocketChannel.open();
        // 2.切换读取模式
        sChannel.configureBlocking(false);
        // 3.绑定连接
        sChannel.bind(new InetSocketAddress(8080));
        // 4.获取选择器
        Selector selector = Selector.open();
        // 5.将通道注册到选择器上,并且指定监听接受事件(客户端的请求已经被注册到了选择器上)
        sChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 6. 轮训式获取"已经准备就绪"的事件(一个一个地判断是否有连接了IO请求)
        while (selector.select() > 0) {
            // 7.获取当前选择器所有注册且监听的事件
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()) {
                // 8.获取准备就绪的事件
                SelectionKey sk = it.next();
                // 9.判断事件是否准备就绪,进入到if代码块的通道都是要被转化成就绪的
                if (sk.isAcceptable()) {
                    // 10.若"就绪",获取客户端连接
                    SocketChannel socketChannel = sChannel.accept();
                    // 11.设置阻塞模式
                    socketChannel.configureBlocking(false);
                    // 12.将该通道注册到服务器上
                    socketChannel.register(selector, SelectionKey.OP_READ);
            // 进入到else if代码块的通道都是已经就绪的 }
else if (sk.isReadable()) { // 13.获取当前选择器"就绪" 状态的通道 SocketChannel socketChannel = (SocketChannel) sk.channel(); // 14.读取数据 ByteBuffer buf = ByteBuffer.allocate(1024); int len = 0; while ((len = socketChannel.read(buf)) > 0) { buf.flip(); System.out.println(new String(buf.array(), 0, len)); buf.clear(); } } it.remove(); } } } }
class Client {

    public static void main(String[] args) throws IOException {
        System.out.println("客户端已经启动....");
        // 1.创建通道
        SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8080));
        // 2.切换异步非阻塞
        sChannel.configureBlocking(false);
        // 3.指定缓冲区大小
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        Scanner scanner=  new Scanner(System.in);
        while (scanner.hasNext()) {
            String str=scanner.next();
            byteBuffer.put((new Date().toString()+"\n"+str).getBytes());
            // 4.切换读取模式
            byteBuffer.flip();
            sChannel.write(byteBuffer);
            byteBuffer.clear();
        }
        sChannel.close();
    }

}
NIO代码看懂即可,但是得学好Netty

 

浅谈NIO(四)

标签:nbsp   close   cte   false   启动   ada   cat   事件   sch   

原文地址:https://www.cnblogs.com/lzh110/p/9481089.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!