array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(异步IO) - 爱码网

Java中有阻塞IO、非阻塞IO。阻塞IO可以理解为“一个连接对应于一线程”。非阻塞IO可以理解为“一个请求(一个请求里面可能会有多个连接【长连接短连接】)对应于一线程”。

BIO

Java中BIO也成为同步阻塞IO。

同步阻塞IO模式下,服务器实现模式为一个连接对应一个线程,即:有连接请求从客户端发起时,服务器端就需要创建一个线程进行处理,如果有大量连接时,服务器就需要创建大量线程进行处理。当然可以通过线程池机制改善。

阻塞IO适用场景为:连接数较小且固定的架构模式,这种方式对服务器资源要求比较高,并发局限于应用中,不建议在生产环境使用。

NIO(非阻塞IO)

Java中非阻塞式IO,也叫同步非阻塞IO,其通过通道和缓冲区来读写数据。通过选择器注册和获取已准备好的感兴趣的通道事件。读数据时,有多少数据就读多少数据,读完立即返回,如SocketChannel和ServerSocketChannel、Selector等。

NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题。NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。

也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

NIO的最重要的地方是,当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面。所以,所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。

在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题。

Java NIO 由以下几个核心部分组成:

  • Channels
  • Buffers
  • Selectors

虽然Java NIO 中除此之外还有很多类和组件,但是,Channel,Buffer和Selector构成了核心的API。其它组件,如Pipe和FileLock,只不过是与三个核心组件共同使用的工具类。

Channel vs Buffer

Channel和Buffer:基本上,所有的IO在NIO中都从一个Channel开始。Channel有点像流,数据可以从Channel读到Buffer中,也可以从Buffer写到Channel中。如:Channel和Buffer有好几种类型。下面是JAVA NIO中的一些主要Channel的实现:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel。这些通道涵盖了UDP 和 TCP 网络IO,以及文件IO。

聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(异步IO)
Java NIO里关键的Buffer实现:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer、MappedByteBuffer(内存映射文件 )。这些Buffer覆盖了能通过IO发送的基本数据类型:byte,short,int,long,float,double和char。

Channel和Buffer使用案例:改代码以随机读写方式打开nio-data.txt文件,一个字节一个字节的把文件内容读入新分配的buf缓存中。

聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(异步IO)

Selector

Selector允许单线程处理多个 Channel。如果应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。单线程中使用一个Selector处理3个Channel的图示:

聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(异步IO)
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

Selector使用案例:该案例使用Selector已订阅事件的通道进行选择,当订阅事件发生时即可另行开辟线程处理该通道事件。
聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(异步IO)

AIO(异步IO)

Java中的异步IO,通过Future轮询和Callback回调两种方式来使用。

与NIO不同,当进行读写操作时,异步IO情况下,我们只需要直接调用API的read或write方法即可。这两个操作都是异步的。

对于读操作而言,当有流可读时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;
对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。
即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道:AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileChannel、AsynchronousDatagramChannel。

Future轮询模式

典型的Java通道类有AsynchronousFileChannel。来看一个例子:
聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(异步IO)
该案例通过异步IO通道AsynchronousFileChannel将文件foobar.txt的内容读入buffer中,在通过Future获取结果。Java将读取文件的操作交给操作系统底层去做,自己只需要知道文件是否读取完毕就好了。其实底层JVM为执行这个任务创建了线程池和通道组。具体可以参考AsynchronousFileChannel的官方说明:

An AsynchronousFileChannel is associated with a thread pool to which tasks are submitted to handle I/O events and dispatch to completion handlers that consume the results of I/O operations on the channel. The completion handler for an I/O operation initiated on a channel is guaranteed to be invoked by one of the threads in the thread pool (This ensures that the completion handler is run by a thread with the expected identity). Where an I/O operation completes immediately, and the initiating thread is itself a thread in the thread pool, then the completion handler may be invoked directly by the initiating thread. When an AsynchronousFileChannel is created without specifying a thread pool then the channel is associated with a system-dependent default thread pool that may be shared with other channels. The default thread pool is configured by the system properties defined by the AsynchronousChannelGroup class.

Callback回调模式

Future其实本质上还是轮循的方式,回调式才是真正的AIO。其基本思想是主线程注册一个CompletionHanlder执行IO操作。CompletionHanlde将带着IO操作的结果返回到主线程中,这个结果会触发它自己的completed或者failed方法(需要重写这两个方法):

  1. void completed(V result, A attachment) - executes if a task completes with a result of type V.
  2. void failed(Throwable e, A attachment) - executes if the task fails to complete due to Throwable e.

callback回调案例:
聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(异步IO)
上面的例子是基于文件的AsynchronousFileChannel,基于网络套接字的AsynchronousServerSocketChannel和AsynchronousSocketChannel也是一样的模式。

喜欢的可以关注微信订阅号:

聊聊Java BIO(同步阻塞IO)、NIO(非阻塞IO)、AIO(异步IO)

或者可以关注头条公众号:

  1. 程序员架构

相关文章: