浅谈NIO(四)

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

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

时间: 2024-08-01 07:47:16

浅谈NIO(四)的相关文章

Office 365 SharePoint 迁移浅谈 (四)使用Migration Tools

下边我们再来谈下微软最近推出的另外一款可以迁移SharePoint 的产品,SharePoint Migration Tools,它的功能会比SharePoint Migration API要强很多,本身也是图形化界面的操作方式,比较简单,但是这个产品目前还只支持SharePoint 2013,目的端也只支持国际版O365,现在还是个测试版本. 之前使用SharePoint Migration API时Version History,权限这些都没办法迁移,但是SharePoint Migrati

浅谈DevExpress&lt;四&gt;:TreeList中的拖拽功能

本篇要实现的目标,简单来说就是把一个treelist的节点用鼠标拖到另外的节点(自身或其他的listview)上,如下图: 1  2  3  首先,在窗口中拉入两个listview,第一个创建三列(上),第二个创建两列(下),如下图: 为第一个listview创建一些节点: 定义一个取得拖拽对象中节点的方法: private TreeListNode GetDragNode(IDataObject data) { return (TreeListNode)data.GetData(typeof(

浅谈Struts(四)

一.Struts2的拦截器(Intercept) 作用:把多个Action中的共有代码,提取至拦截器,从而减少Action中的冗余代码. 1.Action拦截器 a.编写interceptor类 public class MyInterceptor implements Interceptor{ //ActionInvocation 参数的作用: // 1.决定了请求轨迹:ai.invoke(); // 2.获取值栈对象:ValueStack vs = ai.getStack(); // 3.获

浅谈算法和数据结构: 四 快速排序

原文:浅谈算法和数据结构: 四 快速排序 上篇文章介绍了时间复杂度为O(nlgn)的合并排序,本篇文章介绍时间复杂度同样为O(nlgn)但是排序速度比合并排序更快的快速排序(Quick Sort). 快速排序是20世纪科技领域的十大算法之一 ,他由C. A. R. Hoare于1960年提出的一种划分交换排序. 快速排序也是一种采用分治法解决问题的一个典型应用.在很多编程语言中,对数组,列表进行的非稳定排序在内部实现中都使用的是快速排序.而且快速排序在面试中经常会遇到. 本文首先介绍快速排序的思

安卓开发_浅谈Android动画(四)

Property动画 概念:属性动画,即通过改变对象属性的动画. 特点:属性动画真正改变了一个UI控件,包括其事件触发焦点的位置 一.重要的动画类及属性值: 1.  ValueAnimator 基本属性动画类 方法 描述 setDuration(long duration) 设置动画持续时间的方法 setEvaluator(TypeEvaluator value) 设置插值计算的类型 setInterpolator(TimeInterpolator value) 设置时间插值器的类型 addUp

浅谈压缩感知(二十四):压缩感知重构算法之子空间追踪(SP)

主要内容: SP的算法流程 SP的MATLAB实现 一维信号的实验与结果 测量数M与重构成功概率关系的实验与结果 SP与CoSaMP的性能比较 一.SP的算法流程 压缩采样匹配追踪(CoSaMP)与子空间追踪(SP)几乎完全一样,因此算法流程也基本一致. SP与CoSaMP主要区别在于"Ineach iteration, in the SP algorithm, only K new candidates are added, while theCoSAMP algorithm adds 2K

浅谈javascript继承【读javascript设计模式第四章节继承有感】

javascript继承,无任是类式继承,原型式继承还是渗元式继承都是通过不同方法去围绕着prototype转,简单分析下三种不同继承方法是如何围绕prototype转的 一:类似继承,先上关键代码 function extend(subClass,supClass){ var fn = function(){}; fn.prototype = supClass.prototype; subClass.prototype = new fn(); subClass.prototype.constr

浅谈SQL Server中的事务日志(四)----在完整恢复模式下日志的角色

浅谈SQL Server中的事务日志(四)----在完整恢复模式下日志的角色 本篇文章是系列文章中的第四篇,也是最后一篇,本篇文章需要前三篇的文章知识作为基础,前三篇的文章地址如下: 浅谈SQL Server中的事务日志(一)----事务日志的物理和逻辑构架 浅谈SQL Server中的事务日志(二)----事务日志在修改数据时的角色 浅谈SQL Server中的事务日志(三)----在简单恢复模式下日志的角色 简介 生产环境下的数据是如果可以写在资产负债表上的话,我想这个资产所占的数额一定不会

Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理

Java网络编程和NIO详解7:浅谈 Linux 中NIO Selector 的实现原理 转自:https://www.jianshu.com/p/2b71ea919d49 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO https://blog.csdn.net/column/details/21963.html 部分代码会放在我的的Github:https://github.com/h2pl/ 浅谈 Linux