- BIO (blocking IO)即阻塞IO。
java.io包提供了我们最熟知的一些IO功能,比如File抽象、输入输出流等。交互方式是同步、阻塞的方式,也就是说,在读取输入流或者写入输出流时,在读写完成前CPU会将IO任务交给DMA处理,而线程会一直阻塞在那里。
BIO的优点是代码比较简单、直观;缺点是IO效率和扩展性存在局限性,容易称为性能瓶颈。
BIO性能缺陷:BIO会阻塞进程,不适合高并发场景。采用BIO的服务端,通常由要给独立的Acceptor线程负责监听客户端连接。服务端一般在while(true)循环中调用accept()方法等待客户端的连接请求,一旦接收到一个连接请求,就可以建立Socket,并基于Socket进行读写。此时不能在接受其他客户端连接请求,只能等待当前连接操作执行完成。
如果要让BIO通信模型同时处理多个客户端的请求,就必须使用多线程(主要原因是socket.accept()、socket.read()、socket.write()涉及的三个主要函数都是同步阻塞的),会造成不必要的线程开销。不过可以通过线程池机制改善,线程池可以让线程的创建和回收成本相对较低。
即使使用线程池优化也会浪费宝贵的线程资源,并且在百万级并发场景下难以承受。并发量突增可能导致线程数量急剧膨胀以至于线程堆栈溢出、创建新线程失败等故障,最终宕机或僵死,无法提供服务。
- NIO(non-blocking IO)即非阻塞IO。指的是Java1.4引入的Java.nio包。
为了解决BIO的性能问题,Java1.4引入的java.nio包。NIO优化了内存复制以及阻塞导致的严重性能问题。
java.nio包提供了Channel、Selector、Buffer等新的抽象,可以构建多路复用的、同步非阻塞的IO程序,同时提供了更接近操作系统底层的高性能数据操作方式。
使用缓冲区优化读写流
NIO和传统的I/O不同,它是基于块(Block)的,它以块为单位处理数据。在NIO种,最为重要的两个组件时缓冲区(Buffer)和通道(channel)。 Buffer是一块连续的内存块,是NIO读写数据的缓冲。Buffer可以将文件一次性读入内存再做后续处理,而传统的方式是边读文件边处理数据。Channel表示缓冲数据的源头或者目的地,它用于读取缓冲或者写入数据,是访问缓冲的接口。
具体来说,NIO通过缓冲区的好处有以下五点:
1、减少系统调用次数。
传统的IO方式需要频繁在读取内核空间数据的内核态和处理数据的用户态之间切换,需要频繁的系统调用,而线程上下文切换需要大量的计算机资源和时间开销。而NIO通过一次性读或写入大量数据到缓冲区中,可以减少系统调用的次数,从而减少上下文切换。
2、数据缓存与批处理
缓冲区允许程序预读或预写数据。当数据到达时,可以直接写入缓冲区,即使数据没有被处理,也不会阻塞后续的数据读取操作。同样,当需要写数据到输出设备时,可以先写入缓冲区,再由NIO以块的形式传输,这样可以实现数据的批量处理,提高吞吐量。
3、零拷贝
虽然这一特性并非直接由缓冲区提供。但是NIO配合零拷贝技术可以进一步提升效率。在某些场景下,数据可以从缓冲区直接传输到网络接口或者其他进程,无需在用户空间和内核空间之间进行额外的数据复制,减少了内存的复制操作,极大的提高了效率。
4、 异步操作
NIO支持异步I/O,通过缓冲区,可以在数据读写的同时执行其他任务,不必等待I/O完成,这充分的利用率CPU资源,避免了线程在I/O期间的空闲,提高了应用的整体响应速度。
5、可控制的数据处理流程
使用缓冲区可以让开发者更精细的控制数据处理流程,比如精确的控制读写位置、限制读写数量等,这对于高性能的网络编程、文件操作等场景尤为重要。
使用DirectBuffer减少内存复制
NIO还提供了一个可以直接访问物理内存的类DirectBuffer。普通的Buffer分配的是JVM堆内存,而DirectBuffer是直接分配物理内存。
数据要输出到外部设备,必须先从用户空间复制到内核空间,再复制到输出设备,而DirectBuffer则是直接将步骤简化到为从内核空间复制到外部设备,减少了数据拷贝。
由于DirectBuffer申请的是非JVM物理内存,所以创建和销毁的代价很高。DirectBuffer申请的内存并不是直接由JVM负责垃圾回收,但在DirectBuffer包装类被回收时,会通过Java引用机制来释放该内存块。
优化I/O,避免阻塞 为了高效管理非阻塞Channel,NIO引入了Selector(选择器)机制。Selector允许一个单独的线程管理多个Channel,通过注册和监听Channel上的各种I/O事件,应用程序可以以非阻塞的方式高效地处理多个并发的连接。
3、 AIO
AIO即异步非阻塞IO,指的是Java 7 中,对NIO有了进一步的改进,也称为NIO2,引入了异步非阻塞I/O方式。 在Java 7中,NIO有了进一步改进,也就是NIO 2,引入了异步非阻塞IO方式,也有很多人叫它AIO(Asynchronous IO).异步IO操作基于事件和回调机制,可以简单理解为,应用操作直接返回,不会阻塞在那里,当后台处理完成,操作系统会通知响应线程进行后续工作。