java new IO
来源:互联网 发布:生存分析数据库 编辑:程序博客网 时间:2024/06/07 04:30
原文地址:http://bbs.blueidea.com/thread-2679670-1-1.html,本文在此基础上做了一些修改。
当我们的JAVA程序会使用到大量的I/O操作,而程序性能很大一部分受到I/O影响的话,就可以考虑使用NIO来改写旧的I/O了~
所谓的NIO,就是JAVA类里的java.nio这个包~是J2SE1.4以后的版本才有的包~它提供了与传统I/O操作不同的解决方案,在I/O操作较多的时候,能在很大程度上提高程序性能。
基于旧的I/O的传统网络程序描述如下:
1)打开一个ServerSocket监听端口
2)程序在这里阻塞,直到有连接请求
3)读取请求、发送请求
4)关闭连接
5)重复2~4步
这就是传统的阻塞I/O操作,线程没有有效地利用cup,它们将大多数时间花费在I/O的阻塞上,所以这种线程是低效率的。
而非阻塞I/O以新的方式解决了阻塞问题,可以提高系统的效率。
NIO(new I/O)引入了四个概念:
·线性排列的数据缓冲,可读写缓冲
·一个字节码与Unicode字符的字符集映射
·双向通道Channels
·选择器Selectors
下面先来说说数据缓冲Buffer。
在一个Buffer中,position代表当前Buffer第一个能被读写的字节的位置,limit代表第一个不能被读写的字节的位置,capacity代表容量(这里读着有些拗口,是因为Buffer是双向的,如果读者一下子不能明白,可以看后面的解释)。对初始化的Buffer来说,position为0,limit=capacity。
在NIO中,通过Channel读写Buffer的数据。将一个Buffer中的数据写入Channel时必须调用Buffer对象的flip()方法(这里应该搞清楚,是Buffer与Channel交互的时候,才需要flip方法,如果是Buffer内部的自操作,是不需要的)。比如有以下代码:
ByteBuffer bb = ByteBuffer.allcateDirect(1024);
bb.put("this is a test".getBytes());
这两句的意思是:分配一个容量为1024的直接的ByteBuffer,通过put(byte[] b)方法将字符串存入到这个缓冲区bb里。而存在以下代码:
FileOutputStream out = new FileOutputStream("test.txt");
FileChannel channel = out.getChannel();
这时,当我们想把ByteBuffer对象里的数据写入到test.txt时,可以调用FileChannel的write(ByteBuffer)方法,但在这之前一定要先执行bb.flip(),然后再调用channel.write(bb);
flip()方法将缓冲区的position设为0,将limit设为现有数据的最后值。
如果不先调用flip(),则向test.txt里写入的东西就是空的~。
ByteBuffer是Buffer的子类,都位于java.nio包下面,该包下面还封装了其它基本数据类型的buffer,可以通过ByteBuffer的asCharBuffer()等方法得到其它基本类型的Buffer:
java.nio包如下:
ByteOrder 字节顺序的类型安全枚举。
Buffer 一种用于特定的基本类型数据的容器。
ByteBuffer 字节缓冲区。
CharBuffer 字符缓冲区。
DoubleBuffer double缓冲区。
FloatBuffer float缓冲区。
IntBuffer int缓冲区。
LongBuffer long缓冲区。
MappedByteBuffer 直接字节缓冲区,其内容是文件的内存映射区域。
ShortBuffer short缓冲区。
二)字节码与Unicode字符的字符集映射
当需要将CharBuffer的存放的数据编码成ByteBuffer,再将ByteBuffer里存的数据解码成CharBuffer,这时候就要用到java.nio.charset.CharsetEncoder和java.nio.charset.CharsetDecoder了~~
(其实ByteBuffer有个asCharBuffer可以直接将ByteBuffer里的字节数据转换成字符数据的,但Charset类提供各种字符集比如utf-8、iso-8895-1等字符集的映射,可以将CharBuffer里的数据编码成各种格式的编码,然后再由CharsetDecoder解码)!下面是一个完整的程序代码,简单的展示了CharBuffer和ByteBuffer之间编码的转换:
package app.imo;
import java.nio.*;
import java.nio.charset.*;
public class CharsetTest {
private ByteBuffer bb = null;
private String s = "this is a test";
public ByteBuffer encoder(){
CharBuffer cb = CharBuffer.wrap(s);
Charset cs = Charset.forName("UTF-8");
CharsetEncoder ce = cs.newEncoder();
try {
bb = ce.encode(cb); //将CharBuffer编码成ByteBuffer
} catch (CharacterCodingException e) {
e.printStackTrace();
}
return bb;
}
public static void main(String[] args){
CharsetTest ct = new CharsetTest();
ByteBuffer bb = ct.encoder();
Charset c = Charset.forName("UTF-8");
CharsetDecoder cd = c.newDecoder();
CharBuffer cb = null;
try {
cb = cd.decode(bb);
} catch (CharacterCodingException e) {
e.printStackTrace();
}
System.out.println(cb);
}
}
三)NIO的核心技术:Selector、SelectableChannel和SelectionKey
说上面的三个类是NIO的核心技术我觉得一点都不为过~~
因为缓冲技术、字符集映射都是为它们准备的~~
三者位于java.nio.channels这个包里
Selector选择器,它将为我们监听所有的I/O操作,可以通过select()阻塞方法来确定什么时候在Channel中有数据流需要处理。可以由Selector类的静态方法Selector.open()获得一个Selector实例。Selector对象和SelectableChannel对象互为多路复用器。
可以通过某个通道的register()方法注册到已经存在的选择器Selector。register()方法的声明如下:
public final SelectionKey register(Selector sel, int ops):向给定的选择器注册此通道,返回一个选择键。
其中,sel - 要向其注册此通道的选择器,ops - 所得键的可用操作集。
ops可以有四个选择:
SelectionKey.OP_ACCEPT: 用于套接字接受操作的操作集位。
SelectionKey.OP_CONNECT: 用于套接字连接操作的操作集位。
SelectionKey.OP_READ: 用于读取操作的操作集位。
SelectionKey.OP_WRITE: 用于写入操作的操作集位。
register()是SelectableChannel中定义的方法,SelectableChannel是一个抽象类,实现它的子类有DatagramChannel, Pipe.SinkChannel, Pipe.SourceChannel, ServerSocketChannel, SocketChannel。
要生成一个SelecableChannel实例,可以调用其子类的静态方法open(),比如说生成一个ServerSocketChannel实例,可以调用:
ServerSocketChannel ssc = ServerSocketChannel.open();
再通过调用ssc.socket().bind(InetSocketAddress)将ssc绑定到指定ServerSocket。
SelectableChannel的configureBlocking(boolean b)方法,设置为false时即将该通道配置为非阻塞的。
必须将通道配置成非阻塞的,才可以向Selector注册:
ssc.configureBlocking(false);
SelectionKey sk = ssc.register(selector,SelectionKey.OP_ACCEPT);
注册过以后,就可以调用selector.select()方法来选择一组相应的通道已为 I/O操作准备就绪的键了。
下面有二组代码,一组是基于NIO的,另一种是旧I/O的,大家可以对比参考下:
旧I/O客户端:
Socket s = new Socket("localhost",1111);
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
String str;
while((str=in.readLine())!=null){
System.out.println(str);
}
旧I/O服务端:
ServerSocket ss = new ServerSocket(1111);
Socket c = ss.accept();//这里阻塞,标志着每一个ServerSocket都需要阻塞
PrintWriter out = new PrintWriter(c.getOutputStream(),true);
out.write("hello,client".getBytes());
NIO客户端:
SocketChannel channel = SocketChannel.open();
channel.connect(new InetSocketAddress("localhost",1111));
ByteBuffer buffer = ByteBuffer.allocateDirect(512);
channel.read(buffer);
NIO服务端:
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(1111));
ssc.configureBlocking(false);
ssc.register(selector,SelectionKey.OP_ACCEPT);
ssc.select();//这里阻塞,标志着所有ServerSocketChannel可以共用一个阻塞。
java.util.Set keys = ssc.selectedKeys();
java.util.Iterator i = keys.iterator();
while(i.hasNext()){
SelectionKey key = i.next;
i.remove();
ServerSocketChannel serverSocket = (ServerSocketChannel)key.channel();
SocketChannel socket = serverSocket.accept();
ByteBuffer buf = ByteBuffer.wrap("hello,client\n".getBytes());
socket.write(buf);
}
PS:此文一般
当我们的JAVA程序会使用到大量的I/O操作,而程序性能很大一部分受到I/O影响的话,就可以考虑使用NIO来改写旧的I/O了~
所谓的NIO,就是JAVA类里的java.nio这个包~是J2SE1.4以后的版本才有的包~它提供了与传统I/O操作不同的解决方案,在I/O操作较多的时候,能在很大程度上提高程序性能。
基于旧的I/O的传统网络程序描述如下:
1)打开一个ServerSocket监听端口
2)程序在这里阻塞,直到有连接请求
3)读取请求、发送请求
4)关闭连接
5)重复2~4步
这就是传统的阻塞I/O操作,线程没有有效地利用cup,它们将大多数时间花费在I/O的阻塞上,所以这种线程是低效率的。
而非阻塞I/O以新的方式解决了阻塞问题,可以提高系统的效率。
NIO(new I/O)引入了四个概念:
·线性排列的数据缓冲,可读写缓冲
·一个字节码与Unicode字符的字符集映射
·双向通道Channels
·选择器Selectors
下面先来说说数据缓冲Buffer。
在一个Buffer中,position代表当前Buffer第一个能被读写的字节的位置,limit代表第一个不能被读写的字节的位置,capacity代表容量(这里读着有些拗口,是因为Buffer是双向的,如果读者一下子不能明白,可以看后面的解释)。对初始化的Buffer来说,position为0,limit=capacity。
在NIO中,通过Channel读写Buffer的数据。将一个Buffer中的数据写入Channel时必须调用Buffer对象的flip()方法(这里应该搞清楚,是Buffer与Channel交互的时候,才需要flip方法,如果是Buffer内部的自操作,是不需要的)。比如有以下代码:
ByteBuffer bb = ByteBuffer.allcateDirect(1024);
bb.put("this is a test".getBytes());
这两句的意思是:分配一个容量为1024的直接的ByteBuffer,通过put(byte[] b)方法将字符串存入到这个缓冲区bb里。而存在以下代码:
FileOutputStream out = new FileOutputStream("test.txt");
FileChannel channel = out.getChannel();
这时,当我们想把ByteBuffer对象里的数据写入到test.txt时,可以调用FileChannel的write(ByteBuffer)方法,但在这之前一定要先执行bb.flip(),然后再调用channel.write(bb);
flip()方法将缓冲区的position设为0,将limit设为现有数据的最后值。
如果不先调用flip(),则向test.txt里写入的东西就是空的~。
ByteBuffer是Buffer的子类,都位于java.nio包下面,该包下面还封装了其它基本数据类型的buffer,可以通过ByteBuffer的asCharBuffer()等方法得到其它基本类型的Buffer:
java.nio包如下:
ByteOrder 字节顺序的类型安全枚举。
Buffer 一种用于特定的基本类型数据的容器。
ByteBuffer 字节缓冲区。
CharBuffer 字符缓冲区。
DoubleBuffer double缓冲区。
FloatBuffer float缓冲区。
IntBuffer int缓冲区。
LongBuffer long缓冲区。
MappedByteBuffer 直接字节缓冲区,其内容是文件的内存映射区域。
ShortBuffer short缓冲区。
二)字节码与Unicode字符的字符集映射
当需要将CharBuffer的存放的数据编码成ByteBuffer,再将ByteBuffer里存的数据解码成CharBuffer,这时候就要用到java.nio.charset.CharsetEncoder和java.nio.charset.CharsetDecoder了~~
(其实ByteBuffer有个asCharBuffer可以直接将ByteBuffer里的字节数据转换成字符数据的,但Charset类提供各种字符集比如utf-8、iso-8895-1等字符集的映射,可以将CharBuffer里的数据编码成各种格式的编码,然后再由CharsetDecoder解码)!下面是一个完整的程序代码,简单的展示了CharBuffer和ByteBuffer之间编码的转换:
package app.imo;
import java.nio.*;
import java.nio.charset.*;
public class CharsetTest {
private ByteBuffer bb = null;
private String s = "this is a test";
public ByteBuffer encoder(){
CharBuffer cb = CharBuffer.wrap(s);
Charset cs = Charset.forName("UTF-8");
CharsetEncoder ce = cs.newEncoder();
try {
bb = ce.encode(cb); //将CharBuffer编码成ByteBuffer
} catch (CharacterCodingException e) {
e.printStackTrace();
}
return bb;
}
public static void main(String[] args){
CharsetTest ct = new CharsetTest();
ByteBuffer bb = ct.encoder();
Charset c = Charset.forName("UTF-8");
CharsetDecoder cd = c.newDecoder();
CharBuffer cb = null;
try {
cb = cd.decode(bb);
} catch (CharacterCodingException e) {
e.printStackTrace();
}
System.out.println(cb);
}
}
三)NIO的核心技术:Selector、SelectableChannel和SelectionKey
说上面的三个类是NIO的核心技术我觉得一点都不为过~~
因为缓冲技术、字符集映射都是为它们准备的~~
三者位于java.nio.channels这个包里
Selector选择器,它将为我们监听所有的I/O操作,可以通过select()阻塞方法来确定什么时候在Channel中有数据流需要处理。可以由Selector类的静态方法Selector.open()获得一个Selector实例。Selector对象和SelectableChannel对象互为多路复用器。
可以通过某个通道的register()方法注册到已经存在的选择器Selector。register()方法的声明如下:
public final SelectionKey register(Selector sel, int ops):向给定的选择器注册此通道,返回一个选择键。
其中,sel - 要向其注册此通道的选择器,ops - 所得键的可用操作集。
ops可以有四个选择:
SelectionKey.OP_ACCEPT: 用于套接字接受操作的操作集位。
SelectionKey.OP_CONNECT: 用于套接字连接操作的操作集位。
SelectionKey.OP_READ: 用于读取操作的操作集位。
SelectionKey.OP_WRITE: 用于写入操作的操作集位。
register()是SelectableChannel中定义的方法,SelectableChannel是一个抽象类,实现它的子类有DatagramChannel, Pipe.SinkChannel, Pipe.SourceChannel, ServerSocketChannel, SocketChannel。
要生成一个SelecableChannel实例,可以调用其子类的静态方法open(),比如说生成一个ServerSocketChannel实例,可以调用:
ServerSocketChannel ssc = ServerSocketChannel.open();
再通过调用ssc.socket().bind(InetSocketAddress)将ssc绑定到指定ServerSocket。
SelectableChannel的configureBlocking(boolean b)方法,设置为false时即将该通道配置为非阻塞的。
必须将通道配置成非阻塞的,才可以向Selector注册:
ssc.configureBlocking(false);
SelectionKey sk = ssc.register(selector,SelectionKey.OP_ACCEPT);
注册过以后,就可以调用selector.select()方法来选择一组相应的通道已为 I/O操作准备就绪的键了。
下面有二组代码,一组是基于NIO的,另一种是旧I/O的,大家可以对比参考下:
旧I/O客户端:
Socket s = new Socket("localhost",1111);
BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
String str;
while((str=in.readLine())!=null){
System.out.println(str);
}
旧I/O服务端:
ServerSocket ss = new ServerSocket(1111);
Socket c = ss.accept();//这里阻塞,标志着每一个ServerSocket都需要阻塞
PrintWriter out = new PrintWriter(c.getOutputStream(),true);
out.write("hello,client".getBytes());
NIO客户端:
SocketChannel channel = SocketChannel.open();
channel.connect(new InetSocketAddress("localhost",1111));
ByteBuffer buffer = ByteBuffer.allocateDirect(512);
channel.read(buffer);
NIO服务端:
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress(1111));
ssc.configureBlocking(false);
ssc.register(selector,SelectionKey.OP_ACCEPT);
ssc.select();//这里阻塞,标志着所有ServerSocketChannel可以共用一个阻塞。
java.util.Set keys = ssc.selectedKeys();
java.util.Iterator i = keys.iterator();
while(i.hasNext()){
SelectionKey key = i.next;
i.remove();
ServerSocketChannel serverSocket = (ServerSocketChannel)key.channel();
SocketChannel socket = serverSocket.accept();
ByteBuffer buf = ByteBuffer.wrap("hello,client\n".getBytes());
socket.write(buf);
}
http://blog.sina.com.cn/s/blog_4c925dca0100hmvg.html
http://www.goldendoc.org/category/java-nio/
- java new IO
- 【Java】New IO
- NIO简介-Java New IO
- Java New IO(NIO)详解
- Java NIO(New IO) 教程
- Java NIO(New IO) 通俗易懂简明教程
- [Java 12 IO] PrintStream 打印流 new PrintStream(new FileOutputStream(new File("/home。。
- 用Java的New IO开发网络协议
- 用Java的New IO开发网络协议(转)
- 用Java的New IO开发网络协议
- 用Java的New IO开发网络协议
- java IO ——new I/O包
- 尚硅谷java——NIO(New IO)
- 流 JAVA IO 基本小结 通过一行常见的代码讨论:new BufferedReader(new InputStreamReader(System.in))
- 流 JAVA IO 基本小结 通过一行常见的代码讨论:new BufferedReader(new InputStreamReader(System.in))
- new IO学习笔记(一)
- 使用new io 的socket
- New Blog:louchaooo.github.io
- Hibernate使用Criteria实现查询
- 利用VC++实现局域网实时视频传输(网络视频)
- 双向链表结点的插入
- STL易错知识点
- JS_DOM
- java new IO
- Asp封装类的常用函数
- 移动 SVN 仓库,处理 expected fs format between
- oracle10g-对表空间的理解与应用
- 深入理解const char*p,char const*p,char *const p,const char **p,char const**p,char *const*p,char**const p
- datagrideview下拉列表显示数据
- 深入“自顶向下,逐步求精”——面向过程程序设计方法
- 绘制99乘法表,能从最后一行进行删除
- 调试mysql远程访问之心得