java NIO

传统的网络运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作。过去,当打开一个SocketI/O通道后,使用下列语句:

 

Socket socket = new Socket(url.port);

InputStream in = socket.getInputStream();

While(!Thread.interrupted()){

    Int byteRead = in.read();

}

其中,read()守候在端口边不断读取传输过来的字节内容,如果读取不到任何字节内容,read()也只能等待,这会使得整个系统被锁定,影响程序系统继续做其它事情。一个普遍的改进办法就是开设线程,让线程去等待或轮询。但是,这种做法相当消耗资源。更主要的是,当线程轮训后,若有事件发生,只有等到线程下次轮训时才会知道。即没有一种这样的途径:当有事件发生时,能够主动发出通知。

非阻塞I/O作为Reactor模式的实现,实际上提供了一种事件发生、自我激活、主动通知的机制。因此使用非阻塞I/O将大大提高系统的I/O处理能力。

非阻塞I/O中有3个重要的类:SelectorSelectionKeyChannel

Selector实际上是一个Reactor类,主要监视管理一系列SelectionKey,每个SelectionKey代表一种ChannelSelector之间的关系。在某个Channel上如果发生某种连接事件,Selector将会自动激活产生一个SelectionKey对象。即SelectionKey属于事件对象(Event Object),是动态的,每发生一个连接事件就产生一个SelectionKey对象。

从被激活的SelectionKey中,外界可以知道每个Channel发生的具体事件类型。这些事件包括:是否发生连接、是否可以读或者是否可以写等。

从以上分析可以看出,Selector有自我激活的能力。使用Selector时只要告诉它需要关注的特定事件,Selector将会一直监视这种特定事件,一旦发生,就发出通知。类似火警报警器,一旦发生失火事件,立即会主动激活报警。

由于Selector只负责事件发生,不负责事件处理,事件处理是由开发者编制程序实现,因此,使用者需要自己建立一套获取发生事件的机制。

总之,非阻塞I/O的使用包括两大部分:注册事件和获取事件。

首先,需要向Selector注册外界感兴趣的事件,创建Selector对象如下:

Selector selector = Selector.open();

Selector selector = SelectorProvider.provider().openSelecotr();

 

       Selector是一个观察者,那么它观察谁?就是连接通道Channel,这种Channel是一种SelectableChannel,即可以和Selector发生联系的ChannelSelectableChannel常用的有两种:SocketChannelServerSocketChannel,这两种SelectableChannel的区别是可注册的事件不一样。

*         ServerSocketChannel对应事件:OP_ACCEPT

*         SocketChannel对应事件:OP_CONNECTOP_READOP_WRITE

 

前者一般使用在服务器端,可以从中知道有无可以接收的客户端连接。创建ServerSocketChannel如下:

ServerSocketChannel sc = ServerSocketChannel.open();

创建一个ServerSocketChannel后,需要将其和主机端口进行绑定,例如和192.168.0.18009端口绑定:

    InetSocketAddress address = new InetSocketAddress(“192.168.0.1”),8009;

Sc.socket().bind(address);

Sc.configureBlocking(false);

 

现在如果需要从这个ServerSocketChannel了解有无可接受的客户端连接,语法如下:

SelectionKey acceptKey = sc.register(selector,SelectionKey.OP_ACCEPT);

上面一条语句是用Selector注册ServerSocketChannel实例,返回一个Key实例,通常SelectionKey对象都是线程安全型的,但是修改感兴趣的事件操作时,这个方法是被标记为同步的,即在调用interestOps()方法时会锁定一段时间,因此,实际应用中,如果有一个以上的线程来调用同一个Selector对象时,需要使用Selector.wakeup()来解锁。

以上非阻塞I/O的注册事件工作已经准备就绪,那么,在正常运行中,如果Selector发现了事先注册的事件,如何传递出来呢?这就需要建立一个事件获取通道。

其实,获取事件时,只要执行语句selector.select(),这将触发系统内部自动检查所有使用这个Selector注册的Channel状态。如果在某个Channel发现有感兴趣的事件发生了。那么又如何知道是哪个具体Channel发生的呢?

       使用selector.selectedKeys()获取一个SelectionKey结果集,遍历这个结果集,通过每个SelectionKey就可以找到发生事件的Channel,这样可以从这个Channel进行读写数据,如下图所示:

这部分结构主要实现了Reactor模式中的事件到达部分,当有读或写等任何实现注册的事件发生时,可以从Selector中获得相应的SelectionKey,从SelectionKey可以找到相应的Channel,从而能获得客户端发送过来的数据。

03-15
### Java NIO 教程与文档 Java NIO 是一种基于缓冲区和通道的 I/O 处理方式,相较于传统的 Java IO 更高效且适合处理大规模数据传输。以下是关于 Java NIO 的一些核心概念及其学习资源。 #### 1. **核心组件** Java NIO 主要由以下几个部分组成: - **Buffer**: 数据容器,用于存储不同类型的原始数据[^4]。 - **Channel**: 类似于流的概念,但支持双向读写操作。 - **Selector**: 支持多路复用机制,允许单线程管理多个 Channel。 这些组件共同构成了高效的非阻塞 I/O 模型,适用于网络编程和文件操作场景。 #### 2. **官方文档** Oracle 官方文档提供了详尽的 Java NIONIO.2 API 描述以及使用示例。可以通过访问 Oracle 官网并搜索 "Java SE documentation" 来获取最新的官方文档[^3]。 #### 3. **教程推荐** Baeldung 网站上的教程涵盖了 Java NIO 的基本原理及高级应用案例。例如,“Five ways to maximize Java NIO and NIO.2” 提供了五个重要的功能介绍,包括变更通知器(Change Notifiers),这使得监听事件变得更加简单。 #### 4. **实际应用场景** 通过结合实例代码可以更好地理解如何利用 Java NIO 实现高性能的数据传输。例如,在文件写入方面有多种实现方法,既可以直接采用标准库中的 `Files` 工具类,也可以借助更底层的 Buffer 和 Channel 结构完成复杂任务[^1]。 ```java import java.nio.file.*; import java.io.IOException; public class FileWriteExample { public static void main(String[] args) throws IOException { Path path = Paths.get("example.txt"); String content = "This is a test."; // 使用 Files.write 方法快速写入字符串到文件 Files.write(path, content.getBytes()); } } ``` 上述代码展示了如何利用 `Files.write()` 函数向指定路径下的文件写入一段文本内容。 --- ####
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值