在Java1.4之前的版本,Java对I/O的支持并不完善,开发人员在开发高性能I/O程序的时候,会面临以下几个问题:
1、没有数据缓存区,I/O性能存在问题
2、没有C/C++通道的概念,输入和输出流是相互独立的不能复用
3、同步阻塞式I/O通信(BIO),造成线程资源被长时间阻塞(致命缺陷)
4、硬件可移植性差,支持的字符集编码有限
根据UNIX网络编程的概念,I/O模型有五种
详情请查看这篇文章
在没有JavaNIO之前呢,基于Java的所有的所有的Socket通信都采用了同步阻塞式模型(BIO)这种请求<->应答的通信模式降低了开发难度,但是在性能和可靠性方面存在巨大缺陷,因此大型应用服务器都是C/C++语言开发,因为他们可以直接操作系统提供的异步的I/O或者是AIO能力。当并发访问量增大,响应时间延迟增大之后,采用JavaBIO开发的服务器只能通过硬件扩容来满足高并发和低延时,极大增加了企业成本。
正是由于JavaBIO的不足,Java1.4版本之后提供了新的NIO库,支持非阻塞式I/O操作。
JavaNIO提供了很多的异步I/O开发和API类库,主要的类和接口如下:
进行异步I/O操作的缓存区ByteBuffer
进行异步I/O操作的管道Pipe
进行各种I/O操作(异步或者同步)的Channel,包括ServerSocketChannel和SocketChannel
多种字符集编码能力和解码能力
实现非阻塞I/O操作的选择器Selector
基于流行Perl实现的正则表达式库
了解NIO必须先了解几个概念
什么是同步 |
什么是异步 |
什么是阻塞 |
什么是非阻塞 |
什么是同步阻塞 |
什么是同步非阻塞 |
什么是异步阻塞 |
什么是异步非阻塞 |
技术语言解释:
同步:当一个进程去访问另一个进程时,必须得到一个结果(指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪)
异步:当一个进行访问另一个进程时时,不用立刻给结果,我会告诉你一个信息,你把结果放在这里,通知我即可(异步I/O就是Java将I/O读写操作委托给操作系统,告诉操作系统将数据放到哪个缓存区,数据大小)
阻塞:当一个进程去操作某个资源时发现这个资源不在或者暂时不可操作,那么这个进程就一直等,等可以操作
非阻塞:当进程发现没有资源可操作的时候立刻返回读写函数,而不会等待
生活中的例子:
同步:比如电话好友,那么无论打通还是打不通,都会得到回应,(用户接通了,用户正在通话,用户已关机等信息)
异步:比如叫餐,我们APP下单之后就可以做其他的事情,你只需要告诉地址,电话,食物到了送餐员会打电话通知你
阻塞:比如收费站堵车,这个就最能代表阻塞,你只能等着啥干不了
非阻塞:比如到银行排队办理业务,那么我们会拿一张排号小票,就可以去做其他事情,到号就会叫你!
网上有一个例子
同步阻塞:到饭店吃饭点餐,在那里等着不停的问,菜好了吗?菜好了吗?
同步非阻塞:点完餐,直接去打球,不过你需要隔一段时间来问一次,菜好了吗?
异步阻塞:菜做好饭店打电话给你让你亲自去拿。
异步非阻塞:菜做好,饭店直接送到球场
所以我们归纳:
同步和异步是针对应用程序和内核的交互而言的,
1、同步指的是用户进程触发I/O操作并等待或者轮询的去查看I/O操作是否就绪,
2、异步是指用户进程触发I/O操作以后便开始做自己的事情,而当I/O操作已经完成的时候会得到I/O完成的通知。
阻塞和非阻塞是针对于进程在访问数据的时候,根据I/O操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,
1、阻塞方式下读取或者写入函数将一直等待
2、而非阻塞方式下,读取或者写入函数会立即返回一个状态值。
所以,I/O操作可以分为3类:同步阻塞(即早期的I/O操作)、同步非阻塞(NIO)、异步(AIO)。
BIO
在JDK1.4之前,用Java编写网络请求,都是建立一个ServerSocket,然后,客户端建立Socket时就会询问是否有线程可以处理,如果没有,要么等待,要么被拒绝。即:一个连接,要求Server对应一个处理线程。
NIO
在Java里的由来,在JDK1.4及以后版本中提供了一套API来专门操作非阻塞I/O,我们可以在java.nio包及其子包中找到相关的类和接口。由于这套API是JDK新提供的I/O API,因此,也叫New I/O,这就是包名nio的由来。这套API由三个主要的部分组成:缓冲区(Buffers)、通道(Channels)和非阻塞I/O的核心类组成。在理解NIO的时候,需要区分,说的是New I/O还是非阻塞IO,New I/O是Java的包,NIO是非阻塞IO概念。这里讲的是后面一种。
NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题: 在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。
NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。
也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。
AIO
与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。
即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。
在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道:
AsynchronousSocketChannel
AsynchronousServerSocketChannel
AsynchronousFileChannel
AsynchronousDatagramChannel
其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。
原文:http://www.cnblogs.com/gyjx2016/p/7219983.html