一、简介1)Java一共支持3种网络编程模型/IO模式:BIO、NIO、AIO2)JavaBIO:同步和阻塞(传统阻塞)、server实现方式是一个线程一个连接,即当客户端有连接请求时,服务端需要启动一个线程进行处理。如果连接什么都不做,就会造成不必要的线程开销。3)JavaNIO:同步非阻塞,服务端实现模式是一个线程处理多个请求(连接),即客户端发送的连接请求会注册到多路复用器上,多路复用器处理I/O在轮询连接时请求。4)JavaAIO(NIO.2):异步非阻塞,AIO引入异步通道的概念,采用Proactor模式,简化程序编写,只有在有效请求时才启动线程。其特点是完成后通知操作系统服务器端程序启动一个线程进行处理,一般适用于连接数多、连接时间长的应用。2.适用场景1)BIO方式适用于连接数比较少且固定的架构。这种方式对服务器资源要求比较高,并发限制在应用。在JDK1.4之前是唯一的选择,但是程序简单易懂。2)NIO方式适用于连接数较多,连接比较短(轻操作)的架构,比如聊天服务器、弹幕系统、服务器间通信等。编程比较复杂,JDK1.4开始支持。AIO方式用于连接数较多,连接比较长(操作量大)的架构,比如相册服务器,完全调用OS参与并发操作。编程比较复杂,JDK7开始支持了。3.JavaNIO编程3.1JavaNIO基本介绍JavaNIO的全称是javanon-blockingIO,指的是JDK提供的新的API。从JDK1.4开始,Java提供了一系列新的改进的输入/输出特性,统称为NIO(NewIO),是同步非阻塞的。NIO相关类放在java.nio包和子包下,重写了原java.io包中的很多类。NIO有三个核心部分:Channel(通道)、Buffer(缓冲区)、Selector(选择器)NIO是面向缓冲区或面向块的编程。数据被读入一个buffer,供其稍后处理,需要时可以在buffer中来回移动,增加了处理过程的灵活性。使用它可以提供非阻塞的高扩展性网络JavaNIO非阻塞Blocking模式,使得一个线程从某个通道发送请求或者读取数据,但是它只能获取当前可用的数据,如果当前没有数据可用,它不会得到任何东西,而不是保持线程阻塞,所以直到数据发生变化,线程才能继续做其他事情,直到可以读取为止。非阻塞写也是如此。线程请求将一些数据写入通道,但不需要等待它完全写入。线程可以同时做其他事情。3.2NIO与BIO的比较BIO以流式方式处理数据,而NIO以块式方式处理数据。块I/O的效率远高于流I/O。BIO是阻塞的,而NIO是基于字节的非阻塞BIO。Streams和字符流操作,而NIO基于Channel(通道)和Buffer(缓冲区)操作。数据总是从通道读取到缓冲区,或者从缓冲区写入通道。Selector(选择器)用于监听多个通道的事件(例如:连接请求、数据到达等),因此单个线程可以监听多个客户端通道3.3NIO三大核心原理图3.3.1Selector、Channel和Buffer的关系图中每个通道对应一个BufferSelector对应一个线程,一个线程对应多个通道(连接)。这张图体现了选择器注册了三个通道//程序切换到哪个通道是由事件决定的,Event是一个重要的概念。Selector会根据不同的事件在通道之间切换。Buffer是一个内存块。最下面是一个通过Buffer读写数据的数组。这与BIO相同。BIO要么是输入流,要么是输出流,不能是双向的,但是NIOBuffer是可以读写的,需要flip方法来切换通道。是双向的,可以返回底层操作系统的情况,比如Linux。Buffer)3.4.1基本介绍缓冲区(Buffer):缓冲区本质上是一个可以读写数据的内存块,可以理解为一个容器对象(包括数组),它提供了一组方法,方便使用内存块高效,缓冲区对象有内置的机制来跟踪和记录缓冲区的状态变化。Channel提供了从文件和网络读取数据的通道,但是读取或写入的数据必须经过Buffer,如图:3.4.2Buffer类及其子类Buffer类定义了四个buffer,所有的buffer都有属性来提供有关的信息它包含的数据元素:2)Buffer类Write的相关方法列表,而stream只能读或者写?channel可以异步读写数据?channel可以从buffer中读取数据或者向buffer中写入数据:常用的Channel类有:FileChannel、DatagramChannel、ServerSocketChannel和SocketChannel。FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel和SocketChannel用于TCP的数据读写。3.6选择器(selector)3.6.1Java的NIO基本介绍,采用非阻塞IO方式。可以用一个线程处理多个客户端连接,会用到Selector(选择器)Selector可以检测多个注册的channel上是否有事件(注:多个Channel可以以事件Selector的形式注册到同一个channel),如果有事件发生,它会获取该事件,然后对每个事件进行相应的处理。只有当connection/channel真正有读写事件时,才会进行读写,大大降低了系统开销,而且不需要为每个connection创建一个线程,也不需要维护多个线程来避免多线程之间的差距3.6.2Selector图Netty的IO线程NioEventLoop聚合了Selector(选择器,也叫多路复用器),可以并发处理成百上千个客户端连接。当线程从客户端Socket通道读写数据时,如果没有可用数据,线程可以执行其他任务。线程通常将非阻塞IO的空闲时间用于对其他通道执行IO操作,因此单个线程可以管理多个输入和输出通道。由于读写操作是非阻塞的,这样可以充分提高IO线程的运行效率,避免频繁的I/O阻塞导致线程挂起。一个I/O线程可以并发处理N个客户端连接和读写操作,从根本上解决了传统的同步阻塞I/O-连接-线程模型,架构的性能、弹性扩展性和可靠性得到极大提升。很大的推动力。3.6.3Selector方法publicstaticSelectoropen():获取一个selector对象publicintselect(longtimeout):监听所有注册的通道,当可以进行IO操作时,将对应的SelectionKey加入到内部集合中返回,参数是否超时publicSet
