javaNIO浅浅理解

相关问题

IO、BIO、NIO、AIO从java含义代表是什么?
NIO可以对文件操作吗?
BIO有什么缺陷?
NIO为了解决什么问题?
NIO有哪些核心组件以及核心组件内容?

linux网络IO模型有哪些?
NIO-零拷贝是否了解,javaNIO中零拷贝到底省去了那一层级的内存copy?
NIO-epoll机制是否了解?
NIO用到了那个经典技术思想?

基本概念

在这里插入图片描述

同步、异步与阻塞、非阻塞

同步与异步

同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
异步: 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。

阻塞和非阻塞

阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。

IO读写原理

无论是Socket的读写还是文件的读写,在Java层面的应用开发或者是linux系统底层开发,都属于输入input和输出output的处理,简称为IO读写。
先强调一个基础知识:read、write调用,并不是物理设备与内存的直接调用。
read系统调用,是把数据从内核空间的内核缓冲区复制到进程空间进程缓冲区;而write系统调用,是把数据从进程空间的进程缓冲区复制到内核空间的内核缓冲区。这个两个系统调用,都不负责数据在内核缓冲区和磁盘之间的交换。底层的读写交换,是由操作系统kernel内核完成的。
在这里插入图片描述

内核空间[Kernel space]和用户空间[User space]

内核空间是Linux内核运行的空间,而用户空间是用户程序的运行空间,为了保证内核安全,它们之间是隔离的,即使用户的程序崩溃了,内核也不受影响。
在 CPU 的所有指令中,有些指令是非常危险的,如果错用,将导致系统崩溃,比如清内存、设置时钟等。如果允许所有的程序都可以使用这些指令,那么系统崩溃的概率将大大增加。所以linux为了保证安全,设立了以上两个空间。
用户进程通过系统调用访问系统资源的时候,需要切换到内核态,而这对应一些特殊的堆栈和内存环境,必须在系统调用前建立好。而在系统调用结束后,cpu会从内核态切回到用户态,而堆栈又必须恢复成用户进程的上下文。而这种切换就会有大量的耗时。
当进程运行在内核空间时就处于内核态,当进程运行在用户空间时就处于用户态。

内核缓冲区和用户缓存区

缓冲区的目的,是为了减少频繁的系统IO调用。大家都知道,系统调用需要保存之前的进程数据和状态等信息,而结束调用之后回来还需要恢复之前的信息,为了减少这种损耗时间、也损耗性能的系统调用,于是出现了缓冲区。
有了缓冲区,操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区中。等待缓冲区达到一定数量的时候,再进行IO的调用,提升性能。至于什么时候读取和存储则由内核来决定,用户程序不需要关心。
在linux系统中,系统内核也有个缓冲区叫做内核缓冲区。每个进程有自己独立的缓冲区,叫做进程缓冲区。
所以,用户程序的IO读写程序,大多数情况下,并没有进行实际的IO操作,而是在读写自己的进程缓冲区。

Unix网络编程中有五种IO模型

blocking IO(阻塞IO):传统IO模型               
nonblocking IO(非阻塞IO):默认创建的socket都是阻塞的,同步非阻塞IO要求socket被置为NONBLOCK.[这是的NIO并非是java的NIO(new IO)]               
IO multiplexing(多路复用IO):也称为同步【它是在内核态轮询所有socket,伪异步】阻塞IO【阻塞多个socketIO请求】,Java的Selector和linux中的epoll都是这种模型。即经典的reactor设计模式。               
signal driver IO(信号驱动IO)               
asynchronous IO (异步IO):即经典的Proactor模型。也称为异步非阻塞IO。

javaNIO:newIO->自认为是非阻塞IO和多路复用IO的结合体

IO与NIO

传统IO

  • InputStream、OutputStream 基于字节操作的 IO
  • Writer、Reader 基于字符操作的 IO
  • File 基于磁盘操作的 IO
  • Socket 基于网络操作的 IO

javaIO:面向流 、阻塞IO
javaNIO:面向缓存、非阻塞IO、选择器

NIO 方式适用于连接数目多且连接比较短(轻操作)的架构

java的IO模型-IO发展历程 BIO/NIO/AIO

BIO

同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
在这里插入图片描述

采用 BIO 通信模型 的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接。我们一般通过在while(true) 循环中服务端会调用 accept() 方法等待接收客户端的连接的方式监听请求,请求一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成, 不过可以通过多线程来支持多个客户端的连接,如上图所示。
如果要让 BIO 通信模型 能够同时处理多个客户端请求,就必须使用多线程(主要原因是socket.accept()、socket.read()、socket.write() 涉及的三个主要函数都是同步阻塞的),也就是说它在接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的 一请求一应答通信模型 。
在 Java 虚拟机中,线程是宝贵的资源,线程的创建和销毁成本很高,除此之外,线程的切换成本也是很高的。尤其在 Linux 这样的操作系统中,线程本质上就是一个进程,创建和销毁线程都是重量级的系统函数。如果并发访问量增加会导致线程数急剧膨胀可能会导致线程堆栈溢出、创建新线程失败等问题,最终导致进程宕机或者僵死,不能对外提供服务。

可以用线程池替换创建线程,减少线程上下文切换开销;虽有效提高,单并发量大的情况治标不治本

NIO

java1.4引入,为同步非阻塞的IO框架
三大组件:buffer、channel、selelctor
在这里插入图片描述

AIO

JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO2.0。也就是我们要介绍的AIO。NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。异步通道提供两种方式获取操作结果。
(1)通过Java.util.concurrent.Future类来表示异步操作的结果;
(2)在执行异步操作的时候传入一个Java.nio.channels.
CompletionHandler接口的实现类作为操作完成的回调。
NIO2.0的异步套接字通道是真正的异步非阻塞IO,它对应UNIX网络编程中的事件驱动IO(AIO),它不需要通过多路复用器(Selector)对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。
我们可以得出结论:异步Socket Channel是被动执行对象,我们不需要想NIO编程那样创建一个独立的IO线程来处理读写操作。对于AsynchronousServerSocketChannel和AsynchronousSocketChannel,它们都由JDK底层的线程池负责回调并驱动读写操作。正因为如此,基于NIO2.0新的异步非阻塞Channel进行编程比NIO编程更为简单。

在这里插入图片描述

NIO主要内容核心组件

buffer

_在java NIO 中负者数据的存储。缓冲区就是数组。用于存储不同类型的数据。_并提供了对数据结构化访问以及维护读写位置等信息。

常用子类
  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer
buffer核心属性
  • capacity 缓冲区数组的总长度
  • position 下一个要操作的数据元素的位置
  • limit 缓冲区数组中不可操作的下一个元素的位置,limit<=capacity
  • mark 用于记录当前 position 的前一个位置或者默认是 0
0<=mark<=position<=limit<=capacity

在这里插入图片描述

buffer其他特性
  • 基本API
  • 快照buffer->byteBuffer.slice()
  • 只读Buffer->byteBuffer.asReadOnlyBuffer()
  • DirectByteBuffer
  • MappedByteBuffer
  • 零拷贝

channel

我们对数据的读取和写入要通过Channel,它就像水管一样,是一个通道。通道不同于流的地方就是通道是双向的,可以用于读、写和同时读写操作。底层的操作系统的通道一般都是全双工的,所以全双工的Channel比流能更好的映射底层操作系统的API。

主要实现类

FileChannel:用于读取、写入、映射和操作文件的通道。[不能设置非阻塞]
SocketChannel:通过 TCP 读写网络中的数据。
ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
DatagramChannel:通过 UDP 读写网络中的数据通道。

其他特性
  • 基本API:对文件、对网络
  • 通道之间的数据传输
    • _transferFrom():_transferFrom()方法可以将数据从源通道传输到FileChannel中
    • _transferTo():_transferTo()方法将数据从FileChannel传输到其他的channel中
  • 分散(Scatter)与聚集(Gather)

selector

Selector是Java NIO 编程的核心,也是IO多路复用体现,Selector会不断轮询注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作。
在这里插入图片描述

linux底层用的内核函数epoll

SelectionKey

当调用 register(Selector sel, int ops) 将通道注册选择器时,选择器对通道的监听事件,需要通过第二个参数 ops 指定。
可以监听的事件类型(用 可使用 SelectionKey 的四个常量 表示):

  • 读 : SelectionKey.OP_READ (1)
  • 写 : SelectionKey.OP_WRITE (4)
  • 连接 : SelectionKey.OP_CONNECT (8)
  • 接收 : SelectionKey.OP_ACCEPT (16)

如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

javaNIO编写简要步骤【以服务端为例】


// 1.打开ServerSocketChannel,用户监听连接的管道
ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();
// 2.绑定端口,并设置为非阻塞
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(8899));
// 3.创建多路复用器Selector
Selector selector=Selector.open();
// 4.将ServerSocketChannel管道注册到多路复用器Selector上,并指定所关心的事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 5.Selector无限循环准备就绪的key
selector.select();
Set<SelectionKey> selectionKeys=selector.selectedKeys();	
// 6.Selector监听到有新的客户端接入,处理新的接入请求,完成TCP三次握手,并设置非阻塞
// 将新接入的客户端连接注册Selector,监听读操作,用来读取客户端发送的网络消息
ServerSocketChannel serverSocketChannel1= (ServerSocketChannel) selectionKey.channel();
client=serverSocketChannel1.accept();
client.configureBlocking(false);
client.register(selector,SelectionKey.OP_READ);
// 7.读取客户端的消息到buffer中
client= (SocketChannel) selectionKey.channel();
ByteBuffer readBuffer=ByteBuffer.allocate(1024);
int count=client.read(readBuffer);
// 8.最终写入客户端
socketChannel.write(writeBuffer);


epoll简单说下

epoll是一种I/O事件通知机制,是linux 内核实现IO多路复用的一个实现。
IO多路复用是指,在一个操作里同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其的进行读写操作。

数据结构:红黑树加链表
围绕3个API展开:
1.epoll_create:创建实例
2.epoll_ctl:对红黑树进行管理
3.epoll_wait:阻塞等待注册的事件发生


相关测试代码
BioTest.javaChannelScatteringAndGatherIng.javachannelTransfer.javaNioBuffer1.javaNioBuffer2.javaNioBuffer3.javaNioBufferChannel4.javaNioBufferChannel5.javaNioByfferChannel6.javaNIOClient.javaNIOServer.java

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值