首先,我们先澄清一些基本概念:1,区分同步或异步(synchronous/asynchronous)。简单的说,同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步;而异步则相反,其他任务不需要等待当前调用返回,通常依靠事件,回调等机制来实现任务间次序关系。2,区分阻塞与非阻塞(blocking/non-blocking)。在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如 ServerSocket 新连接建立完毕,或数据读取,写入操作完成;而非阻塞则是不管 IO 操作是否结束,直接返回,相应操作在后台继续处理。不能一概而论认为同步或阻塞就是低效,具体还要看应用和系统特征。
第一,IO
Java IO 有很多种,基于不同的 IO 抽象模型和交互方式,可以进行简单区分。首先,传统的 java,io 包,它基于流模型实现,提供了我们最熟知的一些 IO 功能,比如 File 抽象,输入输出流等。交互方式是同步,阻塞的方式,也就是说,在读取输入流或者写入输出流时,在读,写动作完成之前,线程会一直阻塞在哪里,它们之间的调用时可靠的线性顺序。java.io 包的好处是代码比较简单,直观,缺点是 IO 效率和扩展性存在局限性,容易成为应用性能的瓶颈。很多时候,大家把 java.net 下面提供的部分网络 API,比如 Socket,ServerSocket,HttpURLConnection 也归类到同步阻塞 IO 类库,因为网络通信同样是 IO 行为。
对于 IO 的理解我认为至少得需要理解下面几点:1, IO 不仅仅是对文件的操作,网络编程中,比如 Socket 通信,都是典型的 IO 操作目标。2,输入/输出流(InputStreanm/OutputStream)是用于读取或者写入字节的。3,Reader/Writer 则是用于操作字符,增加了字符编解码等功能,适用于类似从文件中读取或者写入文本信息。本质上计算机操作的都是字节,不管是网络通信还是文件读取,Reader/Writer 相当于构建了应用逻辑和原始数据之间的桥梁。4,BufferedOutputStream 等带缓冲区的实现,可以避免频繁的磁盘读写,进而提高 IO 处理效率。这种设计利用了缓冲区,将批量数据进行一次操作,但在使用中千万别忘记了 flush。5,很多 IO 工具类都实现了 Closeable 接口,因为需要进行资源的释放。比如,打开 FileInputStream,它就会获取相应的文件描述符(FileDescriptor),需要利用 try-with-resources,try-finally 等机制保证FileInputStream 被明确关闭,进而相应文件描述符也会失效,否则将导致资源无法被释放。看下图:

第二,NIO
在 java 1.4 中引入了 NIO 框架 (java.nio 包),提供了 Channel,Selector,Buffer 等新的抽象,可以构建多路复用的,同步非阻塞 IO 程序,同时提供了更接近操作系统底层的高性能数据操作方式。在 java 7 中,NIO 有了进一步的改进,也就是 NIO 2,引入了异步非阻塞 IO 方式,也有很多人叫它 AIO(Asynchronous IO)。异步 IO 操作基于事件和回调机制,可以简单理解为,应用操作直接返回,而不是阻塞在哪里,当后台处理完成,操作系统会通知相应线程进行后续工作。
对于 NIO 的理解我认为至少得需要理解下面几部分:1,Buffer,高效的数据容器,除了布尔类型,所有原始数据类型都有相应的 Buffer 实现。2,Channel,类似在 Linux 之类操作系统上看到的文件描述符,是 NIO 中被用来支持批量式 IO 操作的一种抽象。3,File 或者 Socket,通常被认为是比较高层次的抽象,而 Channel 则是更加操作系统底层的一种抽象,这也使得 NIO 得以充分利用现代操作系统底层机制,获得特定场景的性能优化。不同层次的抽象是相互关联的,我们可以通过 Socket 获取 Channel,反之亦然。4,Selector,是 NIO 实现多路复用的基础,它提供了一种高效的机制,可以检测到注册在 Selector 上的多个 Channel 中,是否有 Channel 处于就绪状态,进而实现了单线程对多 Channel 的高效管理。Selector 同样基于底层操作系统机制,不同模式,不同版本都存在区别。
网友评论