【java基础】Nio

阻塞:相对于数据而言,需要一直等待数据准备好才能进行下一步操作(BIO,accept)
非阻塞:不管数据有没有准备好都可以往下执行下一步逻辑

同步:相对于IO操作而言,在某个时间点只发生了一件事情,在IO读写操作的过程中不能再做别的事情
异步:相对于IO操作而言,在某个时间点发生了很多事情,在IO读写操作的瞬间还可以做别的事情

BIO:同步阻塞
NIO:同步非阻塞

这里写图片描述

客户端和服务端之间通信,一条公路只能给一个人用,用完之后就销毁了,如果不断通信,不断开闭,对服务器的压力是非常大的
而且每一个客户端都会在服务端单独起一个线程(Thread)一个端口,当客户端多了,Thread就多了,导致CPU工作会越来越慢
如果客户端不设限,服务端对外开放的端口会被都占用,之后就再也接收不到客户端请求,就发生了阻塞

NIO在BIO上发展而来,因为要提高IO操作的性能。
当服务端启动就在客户端与服务端之间建立好了“康庄大道”,当一个客户端和服务器传输完数据之后,“路”还在,下一个客户端还可以用这条路,这种机制叫做“多路复用技术”
这里写图片描述

服务端不再需要不断的开端口
如果客户端太多,可以适当为通道扩容,通道是一直打开的
这里写图片描述

只会有一个Thread接收,通过Thread死循环和轮询机制,这个线程会一直开着,如果有IO事件到来再去分发处理
有任务来了之后先进入服务大厅给个号等着,所以非阻塞
在IO操作没有完成前,客户端是不能离开服务大厅的,所以同步

任务中的数据先写到buffer中,当数据写好了,线程开始处理
这里写图片描述

Nio-Channel

Channel是一个对象,可以通过它读取和写入数据。可以把它看做IO中的流。但是它和流相比还有一些不同:
1. Channel是双向的,既可以读又可以写,而流是单向的
2. Channel可以进行异步的读写
3. 对Channel的读写必须通过buffer对象
正如上面提到的,所有数据都通过Buffer对象处理,所以,您永远不会将字节直接写入到Channel中,相反,您是将数据写入到Buffer中;同样,您也不会从Channel中读取字节,而是将数据从Channel读入Buffer,再从Buffer获取这个字节。
因为Channel是双向的,所以Channel可以比流更好地反映出底层操作系统的真实情况。特别是在Unix模型中,底层操作系统通常都是双向的。
这里写图片描述
在Java NIO中Channel主要有如下几种类型:
• FileChannel:从文件读取数据的
• DatagramChannel:读写UDP网络协议数据
• SocketChannel:读写TCP网络协议数据
• ServerSocketChannel:可以监听TCP连接

Nio-Selector

选择器是NIO的核心,它是channel的管理者
通过执行select()阻塞方法,监听是否有channel准备好
一旦有数据可读,此方法的返回值是SelectionKey的数量
所以服务端通常会死循环执行select()方法,直到有channl准备就绪,然后开始工作
每个channel都会和Selector绑定一个事件,然后生成一个SelectionKey的对象
需要注意的是:
channel和Selector绑定时,channel必须是非阻塞模式
而FileChannel不能切换到非阻塞模式,因为它不是套接字通道,所以FileChannel不能和Selector绑定事件
在NIO中一共有四种事件:
1.SelectionKey.OP_CONNECT:连接事件
2.SelectionKey.OP_ACCEPT:接收事件
3.SelectionKey.OP_READ:读事件
4.SelectionKey.OP_WRITE:写事件

Nio-buffer

缓冲区buffer主要是和通道数据交互,即从通道中读入数据到缓冲区,和从缓冲区中把数据写入到通道中,通过这样完成对数据的传输。
buffer实际上是一个容器,一个连续数组,它通过几个变量来保存这个数据的当前位置状态。

控制buffer状态的三个变量:
• position:跟踪已经写了多少数据或读了多少数据,它指向的是下一个字节来自哪个位置
• limit:代表还有多少数据可以取出或还有多少空间可以写入,它的值小于等于capacity。
• capacity:代表缓冲区的最大容量,一般新建一个缓冲区的时候,limit的值和capacity的值默认是相等的。

两个方法设置这些值:
flip方法
我们先看一下flip的源码:

public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
 }

最初位置:
这里写图片描述

写入一些数据后
这里写图片描述

调用buffer.flip();方法:
这里写图片描述
调用buffer.flip();方法,这个方法把当前的指针位置position设置成了limit,再将当前指针position指向数据的最开始端,我们现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。
为什么要这样呢?比如需要将缓冲区数据取出来解析(get方法),我现在要读了,就不要再写了,所以调用flip()方法,保存现在状态不再写入数据。

clear方法
先看一下clear的源码:

 public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

这里写图片描述
这个方法重设缓冲区以便接收更多的字节。上图显示了在调用 clear() 后缓冲区的状态。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值