- 浏览: 452216 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (371)
- Java (44)
- Flex (25)
- Design Pattern / UML (5)
- JVM (14)
- Software Engineer (4)
- Testing (30)
- Web Backend (60)
- Linux (7)
- Database (11)
- SOA (28)
- English (6)
- FX (5)
- SAP Support (25)
- SAP Development (5)
- SAP Impl & Oprn (8)
- Articles (15)
- Music (4)
- Recipe (14)
- Multi-thread (4)
- Lucene (2)
- CS basic / Algorithm (12)
- Tour (3)
- C C++ (21)
- Others (10)
- Code Repository C++ (1)
- Code Repository Java (1)
- Code Repository Algorithm (1)
- Code Repository SQL (1)
- Web Frontend (4)
最新评论
-
xiangxm:
Java 强引用、 软引用、 弱引用、虚引用 -
nml-lyb:
123
Mule 的第一个例子 -
nml-lyb:
http://www.baidu.com
Mule 的第一个例子 -
white___ant:
...
Java 强引用、 软引用、 弱引用、虚引用 -
joeyon:
...
Java NIO介绍
前言:
可悲的是我参考的这些技术资料是在02~04年的,那时侯还在上学….
服务器在合理的时间之内处理大量客户机请求的能力取决于服务器使用 I/O
流的效率。同时为成百上千个客户机提供服务的服务器必须能够并发地使用 I/O 服务。Java 平台直到 JDK 1.4(也就是
Merlin)才支持非阻塞 I/O 调用。用 Java
语言写的服务器,由于其线程与客户机之比几乎是一比一,因而易于受到大量线程开销的影响,其结果是既导致了性能问题又缺乏可伸缩性。
为了解决这个问题,Java 平台的最新发行版引入了一组新的类。Merlin 的 java.nio
包充满了解决线程开销问题的技巧,包中最重要的是新的 SelectableChannel 类和 Selector 类。
通道(channel)是客户机和服务器之间的一种通信方式。 选择器(selector)与 Windows
消息循环类似,它从不同客户机捕获各种事件并将它们分派到相应的事件处理程序。在本文,我们将向您展示这两个类如何协同工作,从而为 Java
平台创建非阻塞 I/O 机制。
一:传统的IO
在介绍NIO之前,有必要了解传统的I/O操作的方式。以网络应用为例,传统方式需要监听一个ServerSocket,接受请求的连接为其提供服务(服务通常包括了处理请求并发送响应)
可以分析创建服务器的每个具体步骤。首先创建ServerSocket
ServerSocket server=new ServerSocket(10000);
然后接受新的连接请求
Socket newConnection=server.accept();
对于accept方法的调用将造成阻塞,直到ServerSocket接受到一个连接请求为止。一旦连接请求被接受,服务器可以读客户socket中的请求。
InputStream in = newConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
BufferedReader buffer = new BufferedReader(reader);
Request request = new Request();
while(!request.isComplete()) {
String line = buffer.readLine();
request.addLine(line);
}
这样的操作有两个问题,首先BufferedReader类的readLine()方法在其缓冲区未满时会造成线程阻塞,只有一定数据填满了缓
冲区或者客户关闭了套接字,方法才会返回。其次,它会产生大量的垃圾,BufferedReader创建了缓冲区来从客户套接字读入数据,但是同样创建了
一些字符串存储这些数据。虽然BufferedReader内部提供了StringBuffer处理这一问题,但是所有的String很快变成了垃圾需要
回收。
同样的问题在发送响应代码中也存在
Response response = request.generateResponse();
OutputStream out = newConnection.getOutputStream();
InputStream in = response.getInputStream();
int ch;
while(-1 != (ch = in.read())) {
out.write(ch);
}
newConnection.close();
类似的,读写操作被阻塞而且向流中一次写入一个字符会造成效率低下,所以应该使用缓冲区,但是一旦使用缓冲,流又会产生更多的垃圾。传统的解决
方法通常在Java中处理阻塞I/O要用到线程(大量的线程)。一般是实现一个线程池用来处理请求,线程使得服务器可以处理多个连接,但是它们也同样引发
了许多问题。每个线程拥有自己的栈空间并且占用一些CPU时间,耗费很大,而且很多时间是浪费在阻塞的I/O操作上,没有有效的利用CPU。
问题产生原因:
1。建立连接可能造成阻塞
2。IO处理造成阻塞或者占用大量的cpu和内存
二:改进的IO
1. Buffer
传统的I/O不断的浪费对象资源(通常是String)。新I/O通过使用Buffer读写数据避免了资源浪费。Buffer对象是线性的,有序的数据集合,它根据其类别只包含唯一的数据类型。
java.nio.Buffer 类描述
java.nio.ByteBuffer 包含字节类型。 可以从ReadableByteChannel中读在 WritableByteChannel
中写
java.nio.MappedByteBuffer 包含字节类型,直接在内存某一区域映射
java.nio.CharBuffer 包含字符类型,不能写入通道
java.nio.DoubleBuffer 包含double类型,不能写入通道
java.nio.FloatBuffer 包含float类型
java.nio.IntBuffer 包含int类型
java.nio.LongBuffer 包含long类型
java.nio.ShortBuffer 包含short类型
可以通过调用allocate(int capacity)方法或者allocateDirect(int
capacity)方法分配一个Buffer。特别的,你可以创建MappedBytesBuffer通过调用FileChannel.map(int
mode,long position,int
size)。直接(direct)buffer在内存中分配一段连续的块并使用本地访问方法读写数据。非直接(nondirect)buffer通过使用
Java中的数组访问代码读写数据。有时候必须使用非直接缓冲例如使用任何的wrap方法(如ByteBuffer.wrap(byte[]))在
Java数组基础上创建buffer。
2. 字符编码
向ByteBuffer中存放数据涉及到两个问题:字节的顺序和字符转换。ByteBuffer内部通过ByteOrder类处理了字节顺序问题,但是并没有处理字符转换。事实上,ByteBuffer没有提供方法读写String。
Java.nio.charset.Charset处理了字符转换问题。它通过构造CharsetEncoder和CharsetDecoder将字符序列转换成字节和逆转换。
3. 通道(Channel)
你可能注意到现有的java.io类中没有一个能够读写Buffer类型,所以NIO中提供了Channel类来读写Buffer。通道可以认为是一种连接,可以是到特定设备,程序或者是网络的连接。
ReadableByteChannel和WritableByteChannel分别用于读写。
GatheringByteChannel可以从使用一次将多个Buffer中的数据写入通道,相反的,ScatteringByteChannel则可以一次将数据从通道读入多个Buffer中。你还可以设置通道使其为阻塞或非阻塞I/O操作服务。
为了使通道能够同传统I/O类相容,Channel类提供了静态方法创建Stream或Reader
4. Selector
在过去的阻塞I/O中,我们一般知道什么时候可以向stream中读或写,因为方法调用直到stream准备好时返回。但是使用非阻塞通道,我
们需要一些方法来知道什么时候通道准备好了。在NIO包中,设计Selector就是为了这个目的。SelectableChannel可以注册特定的事
件,而不是在事件发生时通知应用,通道跟踪事件。然后,当应用调用Selector上的任意一个selection方法时,它查看注册了的通道看是否有任
何感兴趣的事件发生。
并不是所有的通道都支持所有的操作。SelectionKey类定义了所有可能的操作位,将要用两次。首先,当应用调用
SelectableChannel.register(Selector sel,int
op)方法注册通道时,它将所需操作作为第二个参数传递到方法中。然后,一旦SelectionKey被选中了,SelectionKey的
readyOps()方法返回所有通道支持操作的数位的和。SelectableChannel的validOps方法返回每个通道允许的操作。注册通道
不支持的操作将引发IllegalArgumentException异常。下表列出了SelectableChannel子类所支持的操作。
ServerSocketChannel OP_ACCEPT
SocketChannel OP_CONNECT, OP_READ, OP_WRITE
DatagramChannel OP_READ, OP_WRITE
Pipe.SourceChannel OP_READ
Pipe.SinkChannel OP_WRITE
三 列一个通讯的例子:简单却实用
服务器:
package
com.sz.nio;
import
java.nio.ByteBuffer;
import
java.nio.IntBuffer;
import
java.nio.channels.SelectionKey;
import
java.nio.channels.Selector;
import
java.nio.channels.ServerSocketChannel;
import
java.nio.channels.SocketChannel;
import
java.nio.channels.spi.SelectorProvider;
import
java.net.InetSocketAddress;
import
java.util.Iterator;
import
java.util.Set;
import
java.io.IOException;
/**
*
SumServer.java
*
Created:
Fri
April
20
13:41:52
2007
*
@author
pineapple
*
@version
1.0
*/
public
class
Server {
private
ByteBuffer
buffer
= ByteBuffer.allocate
(8);
//
新缓冲区的容量,以字节为单位
private
IntBuffer
intBuffer
=
buffer
.asIntBuffer();
private
SocketChannel
socketChannel
=
null
;
private
ServerSocketChannel
serverChannel
=
null
;
/**
*
开启服务
*/
public
void
start() {
try
{
openChannel();
waitForConnection();
}
catch
(IOException e) {
System.
err
.println(e.toString());
}
}
/**
*
打开连接
*
*
@throws
IOException
*/
private
void
openChannel()
throws
IOException {
serverChannel
= ServerSocketChannel.open
();
//
创建Socket连接
serverChannel
.socket().bind(
new
InetSocketAddress(10000));
//
开启端口
serverChannel
.configureBlocking(
false
);
//
设置成为非阻塞模式
System.
out
.println(
"
服务器通道已经打开"
);
}
/**
*
连接接入,处理
*
*
@throws
IOException
*/
private
void
waitForConnection()
throws
IOException {
//
在服务器套接字上注册selector并设置为接受accept方法的通知。
//
这就告诉Selector,套接字想要在accept操作发生时被放在ready表 上,因此,允许多元非阻塞I/O发生。
//Selector selector = Selector.open(); //
这是最简单的创建方法
Selector acceptSelector = SelectorProvider.provider
().openSelector();
//
每个要为客户机请求提供服务的 Channel 都必须接着将自己向Selector 注册。
//Channel
应根据它将处理的事件进行注册。例如,接受传入连接的 Channel应这样注册
//Channel
向 Selector 的注册用 SelectionKey 对象表示。满足以下三个条件之一, Key 就失效:Channel
//
被关闭。Selector 被关闭。通过调用 Key 的 cancel() 方法将 Key 本身取消。
//
如果关键字是可接受(acceptable)的,则接受连接,注册通道,以接受更多的事件(例如:读或写操作)。
//
如果关键字是可读的(readable)或可写的(writable),则服务器会指示它已经就绪于读写本端数据
SelectionKey acceptKey =
serverChannel
.register(acceptSelector,SelectionKey.
OP_ACCEPT
);
int
keysAdded = 0;
//Server
是那个将自己向 Selector 注册以接受所有传入连接的 ServerSocketChannel Server
//
被注册后,我们根据每个关键字(key)的类型以迭代方式对一组关键字进行处理。一个关键字被处理完成后,
//
就都被从就绪关键字(readykeys)列表中除去,如下所示
//select
方法在任何上面注册了的操作发生时返回
//Selector
在 select()调用时阻塞。接着,它开始等待,直到建立了一个新的连接,或者另一个线程将它唤醒,
//
或者另一个线程将原来的阻塞线程中断。某客户已经准备好可以进行I/O操作了,获取其ready键集合
while
((keysAdded = acceptSelector.select()) > 0) {
Set readyKeys = acceptSelector.selectedKeys();
Iterator i = readyKeys.iterator();
//
遍历ready键集合,并处理加法请求
while
(i.hasNext()) {
SelectionKey selectionkey = (SelectionKey) i.next();
i.remove();
ServerSocketChannel nextReady = (ServerSocketChannel) selectionkey.channel();
//
接受加法请求并处理它
// socketChannel = nextReady.accept().socket();
socketChannel
= nextReady.accept();
processRequest();
socketChannel
.close();
}
}
}
private
void
processRequest()
throws
IOException {
buffer
.clear();
socketChannel
.read(
buffer
);
int
result =
intBuffer
.get(0) +
intBuffer
.get(1);
buffer
.flip();
buffer
.clear();
intBuffer
.put(0, result);
socketChannel
.write(
buffer
);
}
public
static
void
main(String[] args) {
new
Server().start();
}
}
客户端:
package
com.sz.nio;
import
java.nio.ByteBuffer;
import
java.nio.IntBuffer;
import
java.nio.channels.SocketChannel;
import
java.net.InetSocketAddress;
import
java.io.IOException;
/**
*
SumClient.java
*
*
*
Created:
Thu
Nov
06
11:26:06
2003
*
*
@author
starchu1981
*
@version
1.0
*/
public
class
Client {
private
ByteBuffer
buffer
= ByteBuffer.allocate
(8);
private
IntBuffer
intBuffer
;
private
SocketChannel
channel
;
/**
*
客户端构造函数
*
*/
public
Client() {
intBuffer
=
buffer
.asIntBuffer();
}
// SumClient constructor
/**
*
连接服务器
发送客户请求
收到服务器响应
*
*
@param
first
*
被计算的数字一
*
@param
second
*
被计算的数字二
*
@return
计算结果
*/
public
int
getSum(
int
first,
int
second) {
int
result = 0;
try
{
channel
= connect();
sendSumRequest(first, second);
result = receiveResponse();
}
catch
(IOException e) {
System.
err
.println(e.toString());
}
finally
{
if
(
channel
!=
null
) {
try
{
channel
.close();
}
catch
(IOException e) {
}
}
}
return
result;
}
/**
*
创建连接
*
*
@return
连接
*
@throws
IOException
*/
private
SocketChannel connect()
throws
IOException {
InetSocketAddress socketAddress =
new
InetSocketAddress(
"localhost"
,
10000);
return
SocketChannel.open
(socketAddress);
}
/**
*
发送请求
*
*
@param
first
*
@param
second
*
@throws
IOException
*/
private
void
sendSumRequest(
int
first,
int
second)
throws
IOException {
buffer
.clear();
intBuffer
.put(0, first);
intBuffer
.put(1, second);
channel
.write(
buffer
);
System.
out
.println(
"
发送加法请求 "
+ first +
"+"
+ second);
}
/**
*
收到响应
*
*
@return
*
@throws
IOException
*/
private
int
receiveResponse()
throws
IOException {
buffer
.clear();
channel
.read(
buffer
);
return
intBuffer
.get(0);
}
public
static
void
main(String[] args) {
Client sumClient =
new
Client();
System.
out
.println(
"
加法结果为 :"
+ sumClient.getSum(100, 324));
}
}
// SumClient
http://blog.csdn.net/PineApple0/archive/2007/04/20/1572394.aspx
发表评论
-
java8 Stream Lazy 解释 (非实现原理)
2015-07-22 07:49 1543http://blog.csdn.net/dm_vincen ... -
java8 系列2 深入理解Java 8 Lambda(类库篇——Streams API,Collectors和并行)
2015-07-22 07:00 2444http://zh.lucida.me/blog/java ... -
java8 系列1 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)
2015-07-22 06:58 1013http://zh.lucida.me/blog/java- ... -
Java 8 Stream探秘 具体实现
2015-07-22 06:31 857http://colobu.com/2014/11/18/J ... -
Arrays.asList() 的了解
2015-07-21 23:05 1130为什么Arrays.asList() 返回的list是 i ... -
java8 Lambda expressions
2015-07-14 13:09 813The single most important chan ... -
java8 new features
2015-07-08 02:06 654http://www.javacodegeeks.com/2 ... -
ReentrantLock与synchronized
2014-09-02 23:34 516http://uule.iteye.com/blog/148 ... -
PriorityQueue的使用
2014-05-29 12:14 894http://blog.csdn.net/yangzhon ... -
java的移位操作详解,左移和右移 << >>, 补码计算
2014-04-17 10:28 766---------------------补码--- ... -
深入Java集合学习系列:ArrayList的实现原理
2014-02-13 09:04 534http://zhangshixi.iteye.com/bl ... -
java集合类 ------------ 实现 细节
2014-02-12 23:12 495深入Java集合学习系列:ArrayList的 ... -
两种简单的方式快速实现hashCode 和 equals方法
2014-02-04 10:46 603http://hi.baidu.com/coolcat_p ... -
集合类 Set
2013-12-30 03:50 471http://www.cnblogs.com/yuchao ... -
java1.5新特性
2013-10-23 10:59 6351.自动包装和解包(Autoboxing and unboxi ... -
正则表达式30分钟入门教程
2013-10-22 00:21 508http://blog.csdn.net/cumt168/a ... -
总结Java方法(函数)传值和传引用的问题
2013-09-16 08:38 577http://java.chinaitlab.com/bas ... -
有状态,无状态对象是什么概念
2013-06-21 06:00 1006http://blog.csdn.net/showwair/ ... -
Dom4j的使用(全而好的文章)
2013-06-09 21:12 507http://xhy0422.iteye.com/blog/ ... -
serialVersionUID 用来表明类的不同版本间的兼容性
2013-06-03 23:12 777http://blog.csdn.net/hulefei29 ...
相关推荐
java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...
Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...
Java NIO英文高清原版
Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据...
java NIO.zip
讲解了 JavaIO 与 JAVA NIO区别,JAVA NIO设计理念,以及JDK中java NIO中语法的使用
Java NIO 深入探讨了 1.4 版的 I/O 新特性,并告诉您如何使用这些特性来极大地提升您所写的 Java 代码的执行效率。这本小册子就程序员所面临的有代表性的 I/O 问题作了详尽阐述,并讲解了 如何才能充分利用新的 I/O ...
Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...
java nio 实现socketjava nio 实现socketjava nio 实现socketjava nio 实现socketjava nio 实现socket
java NIO是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下: – 为所有的原始类型提供 (Buffer) 缓存支持。 – 字符集编码解码解决方案。 – Channel :一个新的原始 I/O 抽象。 – 支持...
java nio入门知识 java nio详细介绍总结
java NIO 的详细介绍文档,javaNIO是从java1.4开始出现的,解决了javaI/O流的复杂度开销,提供了多种简洁高效的I/O操作类
java侧起server(NioUdpServer1.java),基于Java Nio的selector 阻塞等候,一个android app(NioUdpClient1文件夹)和一个java程序(UI.java)作为两个client分别向该server发数据,server收到后分别打印收到的消息...
java NIO技巧及原理解析,java IO原理,NIO框架分析,性能比较
java nio 读文件,java nio 读文件
java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现...
JAVA NIO学习资料JAVA NIO学习资料
基于Java NIO实现五子棋游戏.zip基于Java NIO实现五子棋游戏.zip 基于Java NIO实现五子棋游戏.zip基于Java NIO实现五子棋游戏.zip 基于Java NIO实现五子棋游戏.zip基于Java NIO实现五子棋游戏.zip 基于Java NIO实现...
Java NIO测试示例
java nio编程 非阻塞模式的通信 电子书 带目录标签