- 浏览: 49930 次
- 性别:
文章分类
- 全部博客 (103)
- 一致性哈希算法 (1)
- 云计算 (2)
- Cassandra学习 (2)
- Java网络通信与笔记 (14)
- ZooKeeper学习 (1)
- HBase学习 (1)
- Sqoop学习 (1)
- Java网页开发之 (2)
- Java网络通信框架 (5)
- (memcached)分布式内存对象缓存系统 (1)
- Redis学习 (5)
- Shell学习 (14)
- Linux学习 (10)
- MySQL优化 (17)
- C++ (7)
- HTML5 (5)
- Android学习 (5)
- 网络 (2)
- Node.js (1)
- D3.js (1)
- R语言学习 (3)
- Spark (1)
- CAN协议 (2)
- 解决方案 (0)
最新评论
Java NIO原理图文分析及代码实现
前言:
最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。可以参考:http://baike.baidu.com/view/32726.htm )机制时,发现hadoop的RPC机制的实现主要用到了两个技术:动态代理(动态代理可以参考博客:http://weixiaolu.iteye.com/blog/1477774 )和java NIO。为了能够正确地分析hadoop的RPC源码,我觉得很有必要先研究一下java NIO的原理和具体实现。
这篇博客我主要从两个方向来分析java NIO
目录:
一.java NIO 和阻塞I/O的区别
1. 阻塞I/O通信模型
2. java NIO原理及通信模型
二.java NIO服务端和客户端代码实现
具体分析:
一.java NIO 和阻塞I/O的区别
1. 阻塞I/O通信模型
假如现在你对阻塞I/O已有了一定了解,我们知道阻塞I/O在调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超时)才会返回;同样,在调用ServerSocket.accept()方法时,也会一直阻塞到有客户端连接才会返回,每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。阻塞I/O的通信模型示意图如下:
如果你细细分析,一定会发现阻塞I/O存在一些缺点。根据阻塞I/O通信模型,我总结了它的两点缺点:
1. 当客户端多时,会创建大量的处理线程。且每个线程都要占用栈空间和一些CPU时间
2. 阻塞可能带来频繁的上下文切换,且大部分上下文切换可能是无意义的。
在这种情况下非阻塞式I/O就有了它的应用前景。
2. java NIO原理及通信模型
Java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。下面是java NIO的工作原理:
1. 由一个专门的线程来处理所有的 IO 事件,并负责分发。
2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。
3. 线程通讯:线程之间通过 wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。
阅读过一些资料之后,下面贴出我理解的java NIO的工作原理图:
(注:每个线程的处理流程大概都是读取数据、解码、计算处理、编码、发送响应。)
Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件,这种通信模型是怎么实现的呢?呵呵,我们一起来探究它的奥秘吧。java NIO采用了双向通道(channel)进行数据传输,而不是单向的流(stream),在通道上可以注册我们感兴趣的事件。一共有以下四种事件:
事件名 对应值
服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)
客户端连接服务端事件 SelectionKey.OP_CONNECT(8)
读事件 SelectionKey.OP_READ(1)
写事件 SelectionKey.OP_WRITE(4)
服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,阻塞I/O这时会调用read()方法阻塞地读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到感兴趣的事件到达为止。下面是我理解的java NIO的通信模型示意图:
二.java NIO服务端和客户端代码实现
为了更好地理解java NIO,下面贴出服务端和客户端的简单代码实现。
服务端:
Java代码 收藏代码
package cn.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
* NIO服务端
* @author 小路
*/
public class NIOServer {
//通道管理器
private Selector selector;
/**
* 获得一个ServerSocket通道,并对该通道做一些初始化的工作
* @param port 绑定的端口号
* @throws IOException
*/
public void initServer(int port) throws IOException {
// 获得一个ServerSocket通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// 设置通道为非阻塞
serverChannel.configureBlocking(false);
// 将该通道对应的ServerSocket绑定到port端口
serverChannel.socket().bind(new InetSocketAddress(port));
// 获得一个通道管理器
this.selector = Selector.open();
//将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
//当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
}
/**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
System.out.println("服务端启动成功!");
// 轮询访问selector
while (true) {
//当注册的事件到达时,方法返回;否则,该方法会一直阻塞
selector.select();
// 获得selector中选中的项的迭代器,选中的项为注册的事件
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key,以防重复处理
ite.remove();
// 客户端请求连接事件
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key
.channel();
// 获得和客户端连接的通道
SocketChannel channel = server.accept();
// 设置成非阻塞
channel.configureBlocking(false);
//在这里可以给客户端发送信息哦
channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
//在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ);
// 获得了可读的事件
} else if (key.isReadable()) {
read(key);
}
}
}
}
/**
* 处理读取客户端发来的信息 的事件
* @param key
* @throws IOException
*/
public void read(SelectionKey key) throws IOException{
// 服务器可读取消息:得到事件发生的Socket通道
SocketChannel channel = (SocketChannel) key.channel();
// 创建读取的缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
channel.read(buffer);
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("服务端收到信息:"+msg);
ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
channel.write(outBuffer);// 将消息回送给客户端
}
/**
* 启动服务端测试
* @throws IOException
*/
public static void main(String[] args) throws IOException {
NIOServer server = new NIOServer();
server.initServer(8000);
server.listen();
}
}
客户端:
Java代码 收藏代码
package cn.nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
* NIO客户端
* @author 小路
*/
public class NIOClient {
//通道管理器
private Selector selector;
/**
* 获得一个Socket通道,并对该通道做一些初始化的工作
* @param ip 连接的服务器的ip
* @param port 连接的服务器的端口号
* @throws IOException
*/
public void initClient(String ip,int port) throws IOException {
// 获得一个Socket通道
SocketChannel channel = SocketChannel.open();
// 设置通道为非阻塞
channel.configureBlocking(false);
// 获得一个通道管理器
this.selector = Selector.open();
// 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
//用channel.finishConnect();才能完成连接
channel.connect(new InetSocketAddress(ip,port));
//将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
channel.register(selector, SelectionKey.OP_CONNECT);
}
/**
* 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
* @throws IOException
*/
@SuppressWarnings("unchecked")
public void listen() throws IOException {
// 轮询访问selector
while (true) {
selector.select();
// 获得selector中选中的项的迭代器
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key,以防重复处理
ite.remove();
// 连接事件发生
if (key.isConnectable()) {
SocketChannel channel = (SocketChannel) key
.channel();
// 如果正在连接,则完成连接
if(channel.isConnectionPending()){
channel.finishConnect();
}
// 设置成非阻塞
channel.configureBlocking(false);
//在这里可以给服务端发送信息哦
channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
//在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ);
// 获得了可读的事件
} else if (key.isReadable()) {
read(key);
}
}
}
}
/**
* 处理读取服务端发来的信息 的事件
* @param key
* @throws IOException
*/
public void read(SelectionKey key) throws IOException{
//和服务端的read方法一样
}
/**
* 启动客户端测试
* @throws IOException
*/
public static void main(String[] args) throws IOException {
NIOClient client = new NIOClient();
client.initClient("localhost",8000);
client.listen();
}
}
发表评论
-
Java之Concurrent(并发)包
2015-09-25 16:31 310因为实在是太多了,太长了,一时间无法自习整理和消化,先 ... -
Java集合框架
2015-09-25 16:31 384集合框架有很多的 ... -
final, finally, finalize的区别
2015-09-19 11:39 3831.final final修饰类,说明 ... -
四种会话跟踪技术
2015-09-19 11:36 767会话跟踪是一种灵活、轻便的机制,它使Web上的状态编程变为可能 ... -
java中volatile关键字的含义
2015-09-19 10:59 372在java线程并发处理中, ... -
java笔记--关于线程同步(5种同步方式)
2015-09-19 10:21 407[size=medium]为何要使用同步? java ... -
RPC
2015-07-30 11:04 382RPC 功能目标 RPC 的 ... -
NIO学习(一)
2015-07-22 10:43 322[size=medium]NIO学习 一.NIO中的几个基础 ... -
Java IO浅谈
2015-07-21 13:12 298Java IO浅谈 [size=large] ... -
线程池(二)
2015-07-21 12:29 357Java ... -
线程池(一)
2015-07-21 12:11 358线程池 在上一篇博客中,我们有提及阻塞IO的为每个连接请求 ... -
多种IO设计模式
2015-07-21 11:45 446Java通信的几种IO设计 [size=large]阻塞IO ... -
阻塞与非阻塞-----同步与异步
2015-07-21 11:31 475阻塞(blocking)与非阻塞(non-blocking)I ...
相关推荐
### Java NIO原理 图文分析及代码实现 #### 前言 在深入探讨Java NIO之前,我们先简要回顾一下NIO的概念及其引入的原因。随着互联网的发展,越来越多的应用程序需要处理高并发的网络连接请求。传统的阻塞I/O模型在...
Java NIO(New IO)是Java 1.4版本引入的一个新API,全称为Non-blocking Input/Output,它提供了一种不同于传统IO的编程模型,传统IO...在实际编码时,参考博文链接中的代码实例,可以帮助你更好地理解和实践Java NIO。
- **Java NIO原理及通信模型**:NIO采用了非阻塞的方式,通过一个单独的线程来处理所有IO事件,使用事件驱动机制,当事件发生时才进行处理。它依赖于选择器(Selector)来监控多个通道(Channel)上的事件,如连接...
压缩包中的文件可能包含了实现这三种通信模型的Java代码示例,通过学习和理解这些代码,你可以更好地掌握Java I/O的运用,并根据需求选择合适的模型。建议从简单的BIO开始,逐步过渡到更复杂的NIO和AIO,理解其工作...
4. **文件系统操作**:Java NIO提供了FileChannel用于文件的读写,可以实现大文件的高效传输。例如,使用transferTo和transferFrom方法可以直接将文件数据从一个通道传输到另一个通道,无需额外的缓冲区。 5. **...
下面将详细介绍Java NIO的主要组件和工作原理,并结合这两个文件名推测它们可能包含的内容。 1. **Selector(选择器)**:选择器是NIO的核心组件,它能够监控多个通道(Channel)的状态变化,当某个通道准备进行...
### Java NIO 原理与使用详解 #### 一、Java NIO 概述 在深入了解 Java NIO 的工作原理及其使用之前,我们首先来了解一下什么是 Java NIO(New I/O)。Java NIO 是 Java SE 1.4 版本引入的一个全新的 I/O API,...
Java NIO,即Non-Blocking I/O,是Java在JDK 1.4引入的一套新的I/O API,旨在提供一种更加高效的方式来处理I/O操作,尤其是对于网络编程和高并发场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器...
Java NIO提供了FileChannel,可以实现高效的大文件读写。例如,可以使用FileChannel的transferTo和transferFrom方法进行文件间的零拷贝操作,减少系统调用,提高效率。 **6. 多路复用** Java NIO的选择器使得单...
Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。 Java NIO...
Java NIO(New IO)是Java 1.4版本引入的一种新的I/O API,它提供了非阻塞I/O操作的能力,极大地提升了Java在...通过分析和学习这个源码,开发者可以深入理解Java NIO的工作原理,并将其应用于实际的网络编程项目中。
- **Selector 基础**:介绍了 Selector 的基本概念及工作原理。 - **使用 SelectionKey**:解释了如何使用 SelectionKey 来管理 Channel 的选择状态。 - **使用 Selectors**:详细讨论了如何使用 Selector 来监听多...
本文将深入探讨Java中的NIO,包括它与IO的区别、工作原理以及如何通过代码实现NIO操作。 NIO是Java中一个强大的IO处理机制,它通过通道、缓冲区和选择器提供了一种高效、灵活的数据处理方式。虽然它的编程模型比传统...
Java NIO(New IO)是Java 1.4版本引入的一个新模块,全称为New Input/Output,是对传统IO API的扩展...通过分析和理解这个项目,开发者不仅可以深入理解NIO的工作原理,还能掌握如何将NIO应用到实际的并发网络编程中。
`nio.c`可能是指C语言实现的NIO相关代码,这在Java NIO中不太常见,但可能是为了演示底层I/O操作的原理或与Java NIO进行性能比较。在Java中,NIO API通常由Java标准库提供,无需使用C代码。 `java_nio_chm`指的是...
Java NIO,全称为New Input/Output,是Java在1.4版本引入的一个新特性,旨在提供一种更高效、更具选择性的I/O模型。...通过分析NIO源码,我们可以深入了解其内部工作原理,进一步优化和调试相关代码。
### Java NIO与ByteBuffer详解 #### 一、Java NIO简介 Java NIO(New I/O)是Java SE 1.4版本引入的一种新的I/O处理方式,它提供了与传统BIO(Blocking I/O)不同的数据处理机制。NIO的核心组件包括`Channels`、`...
Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java从JDK 1.4版本开始引入的一种I/O模型,它为Java提供了更高效的数据传输能力,尤其适用于高并发的I/O操作场景。与传统的 Blocking I/O 相比,...