《Java核心密卷36讲》11-12讲学习笔记

目录

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

Java有几种文件拷贝方式?哪一种最高效?


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、Bufer等新的抽象,可以构建多路复用的、同步非阻塞IO程序,同时提供了更接近操作系统底层 的高性能数据操作方式

 

第三,

在Java 7中,NIO有了进一步的改进,也就是NIO 2,引入了异步非阻塞IO方式,也有很多人叫它AIO(Asynchronous IO)

异步IO操作基于事件和回调机制,可以简单 理解为,应用操作直接返回,而不会阻塞在那里,当后台处理完成,操作系统会通知相应线程进行后续工作

知识扩展

区分同步或异步(synchronous/asynchronous)。

简单来说,同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步

而异步则相反,其他任务不需要等待当前调用返回,通常依靠事件、回调等机制来实现任务间次序关系

 

区分阻塞与非阻塞(blocking/non-blocking)。

在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如ServerSocket新连接建立 完毕,或数据读取、写入操作完成

而非阻塞则是不管IO操作是否结束,直接返回,相应操作在后台继续处理

不能一概而论认为同步或阻塞就是低效,具体还要看应用和系统特征。

 

对于java.io,我们都非常熟悉,我这里就从总体上进行一下总结。总体上,我认为你至少需要理解:

IO不仅仅是对文件的操作,网络编程中,比如Socket通信,都是典型的IO操作目标

输入流、输出流(InputStream/OutputStream)是用于读取或写入字节的,例如操作图片文件

而Reader/Writer则是用于操作字符,增加了字符编解码等功能,适用于类似从文件中读取或者写入文本信息。本质上计算机操作的都是字节,不管是网络通信还是文件读 取,Reader/Writer相当于构建了应用逻辑和原始数据之间的桥梁

BuferedOutputStream等带缓冲区的实现,可以避免频繁的磁盘读写,进而提高IO处理效率。这种设计利用了缓冲区,将批量数据进行一次操作,但在使用中千万别忘 了fush。

参考下面这张类图,很多IO工具类都实现了Closeable接口,因为需要进行资源的释放。比如,打开FileInputStream,它就会获取相应的文件描述符(FileDescriptor),需要 利用try-with-resources、 try-fnally等机制保证FileInputStream被明确关闭,进而相应文件描述符也会失效,否则将导致资源无法被释放。利用专栏前面的内容提到 的Cleaner或fnalize机制作为资源释放的最后把关,也是必要的。

NIO的主要组成部分:

Bufer,

高效的数据容器除了布尔类型,所有原始数据类型都有相应的Bufer实现

 

Channel,

类似在Linux之类操作系统上看到的文件描述符,是NIO中被用来支持批量式IO操作的一种抽象

 

File或者Socket,

通常被认为是比较高层次的抽象,而Channel则是更加操作系统底层的一种抽象,这也使得NIO得以充分利用现代操作系统底层机制,获得特定场景的性能优 化,例如,DMA(Direct Memory Access)等。不同层次的抽象是相互关联的,我们可以通过Socket获取Channel,反之亦然。

 

Selector,

是NIO实现多路复用的基础,它提供了一种高效的机制,可以检测到注册在Selector上的多个Channel中,是否有Channel处于就绪状态,进而实现了单线程对 多Channel的高效管理。 Selector同样是基于底层操作系统机制,不同模式、不同版本都存在区别。

 

Chartset,

提供Unicode字符串定义,NIO也提供了相应的编解码器等,例如,通过下面的方式进行字符串到ByteBufer的转换: Charset.defaultCharset().encode("Hello world!"));

NIO能解决什么问题

IO都是同步阻塞模式,所以需要多线程以实现多任务处理

而NIO则是利用了单线程轮询事件的机制,通过高效地定位就绪的Channel,来决定做什 么,仅仅select阶段是阻塞的可以有效避免大量客户端连接时,频繁线程切换带来的问题,应用的扩展能力有了非常大的提高

Java有几种文件拷贝方式?哪一种最高效?

典型回答

Java有多种比较典型的文件拷贝实现方式,比如:

 

利用java.io类库,直接为源文件构建一个FileInputStream读取,然后再为目标文件构建一个FileOutputStream,完成写入工作。

利用java.nio类库提供的transferTo或transferFrom方法实现。

Java标准类库本身已经提供了几种Files.copy的实现。

对于Copy的效率,这个其实与操作系统和配置等情况相关,总体上来说,NIO transferTo/From的方式可能更快,因为它更能利用现代操作系统底层机制,避免不必要拷贝和上下 文切换

知识扩展

拷贝实现机制分析

先来理解一下,前面实现的不同拷贝方法,本质上有什么明显的区别。

内核态空间(Kernel Space),这是操作系统层面的基本概念,操作系统内核、硬件驱动等运行在内核态空间,具有相对高的特 权;

用户态空间(User Space),则是给普通应用和服务使用。

当我们使用输入输出流进行读写时,实际上是进行了多次上下文切换,比如应用读取数据时,先在内核态将数据从磁盘读取到内核缓存,再切换到用户态将数据从内核缓存读取到用 户缓存。 写入操作也是类似,仅仅是步骤相反,你可以参考下面这张图。

这种方式会带来一定的额外开销,可能会降低IO效率

 

而基于NIO transferTo的实现方式,在Linux和Unix上,则会使用到零拷贝技术数据传输并不需要用户态参与,省去了上下文切换的开销和不必要的内存拷贝,进而可能提高应用 拷贝性能

注意,transferTo不仅仅是可以用在文件拷贝中,与其类似的,例如读取磁盘文件,然后进行Socket发送,同样可以享受这种机制带来的性能和扩展性提高

transferTo的传输过程是:

 

 

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值