Java 核心技术面试精讲
杨晓峰
前 Oracle 首席工程师
125942 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 44 讲
Java 核心技术面试精讲
15
15
1.0x
00:00/00:00
登录|注册

第11讲 | Java提供了哪些IO方式? NIO如何实现多路复用?

CompletionHandler回调接口
异步操作
线程上下文切换开销
避免频繁创建、销毁线程的开销
多路复用机制
Charset
Selector
Channel
Buffer
Closeable接口
BufferedOutputStream
Reader/Writer
输入流、输出流
基于事件和回调机制
引入了异步非阻塞IO方式
提供更接近操作系统底层的高性能数据操作方式
实现多路复用的、同步非阻塞IO程序
提供Channel、Selector、Buffer等新的抽象
归类到同步阻塞IO类库
Socket、ServerSocket、HttpURLConnection
IO效率和扩展性存在局限性
简单、直观的代码
同步、阻塞的方式
提供File抽象、输入输出流等
基于流模型实现
AIO实现
NIO多路复用的局限性
NIO能解决什么问题?
Java NIO概览
java.io的基本概念
区分阻塞与非阻塞
区分同步或异步
Java 7中的NIO 2(AIO)
Java 1.4中引入的NIO框架(java.nio包)
java.net下的网络API
传统的java.io包
知识扩展
Java提供的IO方式

该思维导图由 AI 生成,仅供参考

IO 一直是软件开发中的核心部分之一,伴随着海量数据增长和分布式系统的发展,IO 扩展能力愈发重要。幸运的是,Java 平台 IO 机制经过不断完善,虽然在某些方面仍有不足,但已经在实践中证明了其构建高扩展性应用的能力。
今天我要问你的问题是,Java 提供了哪些 IO 方式? NIO 如何实现多路复用?

典型回答

Java IO 方式有很多种,基于不同的 IO 抽象模型和交互方式,可以进行简单区分。
第一,传统的 java.io 包,它基于流模型实现,提供了我们最熟知的一些 IO 功能,比如 File 抽象、输入输出流等。交互方式是同步、阻塞的方式,也就是说,在读取输入流或者写入输出流时,在读、写动作完成之前,线程会一直阻塞在那里,它们之间的调用是可靠的线性顺序。
java.io 包的好处是代码比较简单、直观,缺点则是 IO 效率和扩展性存在局限性,容易成为应用性能的瓶颈。
很多时候,人们也把 java.net 下面提供的部分网络 API,比如 Socket、ServerSocket、HttpURLConnection 也归类到同步阻塞 IO 类库,因为网络通信同样是 IO 行为。
第二,在 Java 1.4 中引入了 NIO 框架(java.nio 包),提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层的高性能数据操作方式。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Java的IO机制经历了传统同步IO和较新的NIO框架的发展,为读者提供了更多可能性。传统的java.io包提供了熟知的IO功能,但在处理大规模数据和分布式系统时存在局限性。相比之下,NIO框架引入了Channel、Selector、Buffer等新的抽象,可以构建多路复用的、同步非阻塞IO程序,提供了更接近操作系统底层的高性能数据操作方式。在Java 7中,NIO有了进一步的改进,引入了异步非阻塞IO方式,也称为AIO,实现了应用操作的非阻塞处理。NIO的主要组成部分包括Buffer、Channel、Selector和Charset,它们提供了高效的数据容器、底层抽象和编解码器等功能。NIO通过多路复用机制解决了同步阻塞方式的低扩展性问题,提高了应用的扩展能力。相比之下,AIO引入了异步IO模式,利用事件和回调处理操作,进一步提升了IO处理的效率。通过比较传统IO和NIO的实现方式,以及NIO在处理大规模数据和高并发情况下的优势,读者可以更直观地了解Java IO机制的发展和应用。同时,通过示例代码和场景分析,读者可以更好地了解NIO和AIO的工作原理和优势,为他们在实际应用中选择合适的IO方式提供了参考。文章通过深入浅出的方式介绍了Java IO机制的发展和特点,为读者提供了全面的了解和实际应用的指导。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 核心技术面试精讲》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(74)

  • 最新
  • 精选
  • 雷霹雳的爸爸
    批评NIO确实要小心,我觉得主要是三方面,首先是如果是从写BIO过来的同学,需要有一个巨大的观念上的转变,要清楚网络就是并非时刻可读可写,我们用NIO就是在认真的面对这个问题,别把channel当流往死里用,没读出来写不进去的时候,就是该考虑让度线程资源了,第二点是NIO在不同的平台上的实现方式是不一样的,如果你工作用电脑是win,生产是linux,那么建议直接在linux上调试和测试,第三点,概念上的,理解了会在各方面都有益处,NIO在IO操作本身上还是阻塞的,也就是他还是同步IO,AIO读写行为的回调才是异步IO,而这个真正实现,还是看系统底层的,写完之后,我觉得我这一二三有点凑数的嫌疑

    作者回复: 不错,不过,在非常有必要之前,不见得都要底层,毕竟各种抽象,都是为特定领域工程师准备的,JMM等抽象都是为了大家有个清晰的、不同层面的高效交流

    2018-05-29
    57
  • Chan
    忘记回答问题了。所以对于多路复用IO,当出现有的IO请求在数据拷贝阶段,会出现由于资源类型过份庞大而导致线程长期阻塞,最后造成性能瓶颈的情况

    作者回复: 对

    2018-06-16
    2
    38
  • 扁担
    据我理解,NIO2的局限性在于,如果回调时客户端做了重操作,就会影响调度,导致后续的client回调缓慢

    作者回复: 对,这是这种多路复用的主要局限之一,nodejs等其他类似框架都有这问题

    2018-10-21
    3
    18
  • 扁扁圆圆
    这里Nio的Selector只注册了一个sever chanel,这没有实现多路复用吧,多路复用不是注册了多个channel ,处理就绪的吗?而且处理客户端请求也是在同线程内,这还不如上面给的Bio解决方案吧

    作者回复: 这是简化的例子,少占篇幅

    2018-06-02
    2
    11
  • yxw
    java nio的selector主要的问题是效率,当并发连接数达到数万甚至数十万的时候 ,单线程的selector会是一个瓶颈;另一个问题就是再线上运行过程中经常出现cpu占用100%的情况,原因也是由于selector依赖的操作系统底层机制bug 导致的selector假死,需要程序重建selector来解决,这个问题再jdk中似乎并没有很好的解决,netty成为了线上更加可靠的网络框架。不知理解的是否正确,请老师指教。

    作者回复: 嗯,有局限性;那个epoll的bug应该在8里修了,netty的改进不止那些,它为了性能改了很多底层,后面会介绍,好多算是hack;另外nio的目的是通用场景的基础API,和终端应用有个距离,核心类库很多都是如此定位,netty这种开源框架更贴近用户场景

    2018-06-03
    9
  • zjh
    看nio代码部分,请求接受和处理都是一个线程在做。这样的话,如果有多个请求过来都是按顺序处理吧,其中一个处理时间比较耗时的话那所有请求不都卡住了吗?如果把nio的处理部分也改成多线程会有什么问题吗

    作者回复: 这种情况需要考虑把耗时操作并发处理,再说处理是费cpu,还是重io,需要不同处理;如果耗时操作非常多,就不符合这种模型的适用场景

    2018-05-31
    9
  • lorancechen
    我也自己写过一个基于nio2的网络程序,觉得配合futrue写起来很舒服。 仓库地址:https://github.com/LoranceChen/RxSocket 欢迎相互交流开发经验~ 记得在netty中,有一个搁置的netty5.x项目被废弃掉了,原因有一点官方说是性能提升不明显,这是可以理解的,因为linux下是基于epoll,本质还是select操作。 听了课程之后,有一点印象比较深刻,select模式是使用一个线程做监听,而bio每次来一个链接都要做线程切换,所以节省的时间在线程切换上,当然如果是c/c++实现,原理也是一样的。 想问一个一直困惑的问题,select内部如何实现的呢? 个人猜测:不考虑内核,应用层的区分,单纯从代码角度考虑,我猜测,当select开始工作时,有一个定时器,比如每10ms去检查一下网络缓冲区中是否有tcp的链接请求包,然后把这些包筛选出来,作为一个集合(即代码中的迭代器)填入java select类的一个集合成员中,然后唤醒select线程,做一个while遍历处理链接请求,这样一次线程调度就可以处理10ms内的所有链接。与bio比,节省的时间在线程上下文切换上。不知道这么理解对不对。 另外,也希望能出一个课程,按照上面这种理解底层的方式,讲讲select(因为我平常工作在linux机器,所以对select epoll比较感兴趣)如何处理read,write操作的。谢谢~

    作者回复: 坦白说,内核epoll之类实现细节目前我的理解也有限

    2018-05-31
    9
  • 萧萧
    作者对同步/异步, 阻塞/非阻塞的概念说明存在问题。 《操作系统(第9版)》中关于进程通信中有对这部分概念做过解释, 在进程间通信的维度, 同步和阻塞,异步和非阻塞是相同的概念。 沿着作者的概念解释简单推论一下就可以发现: 如果同步操作是需要等待调用返回才能进行下一步, 显然这个调用是阻塞的。 反之, 不需要等待调用返回的接口,必然需要提供事件, 回调等机制,这种调用显然是非阻塞的。

    作者回复: 这东西并没有完全共识,概念定义要看上下文,很多情况下可以算是同等,但在网络IO编程中是区分的,本文的关注点就是这个

    2018-08-08
    3
    8
  • lorancechen
    还有一个问题请教,select在单线程下处理监听任务是否会成为瓶颈?能否通过创建多个select实例,并发监听socket事件呢?

    作者回复: Doug Lea曾经推荐过多个Selector,也就是多个reactor,如果你是这意思

    2018-05-31
    7
  • 残月@诗雨
    杨老师,有个问题一直不太明白:BufferedInputStream和普通的InputStream直接read到一个缓冲数组这两种方式有什么区别?

    作者回复: 我理解是bufferedIS是内部预读,所以两个buffer的意义不一样,前面是减少磁盘之类操作

    2018-05-29
    2
    5
收起评论
显示
设置
留言
74
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部