1.NIO与传统IO的比较

  Java的NIO(New IO)是不同于旧IO的,旧的IO是基于字节流和字符流的,是阻塞的IO。NIO是基于通道(Channel)和缓冲区(Buffer)的,是非阻塞的IO。

  使用旧IO每次读取一行数据流的流程图如下,Thread必须等待,等待readline读到一行的数据并返回。

  

  使用NIO,可以周期判断Buffer中是否有数据,没有数据时还可以去做其它的事情。基于缓存(Buffer)在一定程度上减少了读写速度不一致所带来的等待。

    

  NIO可以使用单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,实现NIO的服务器可能是一个优势。

  

  如果有少量的连接使用非常高的带宽,一次发送大量的数据,旧IO模型实现可能非常契合。

  

  Java中几种IO的比较:  

  BIO:传统的IO,是同步阻塞IO

  NIO:同步非阻塞IO,轮询缓冲区的状态。

  AIO:  jdk1.7开始支持,异步非阻塞IO

 2.Channel、Buffer和Selector

  NIO的核心组成部分:Channel、Buffer和Selector。

  选择器(Selector)用于监听多通道中的事件。

 

  Channel的主要实现:

  • FileChannel  : 从文件中读写数据
  • DatagramChannel  :  基于UDP读写网络中的数据
  • SocketChannel : 基于TCP读写网络中的数据
  • ServerSocketChannel  : 监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

  Buffer的主要实现: 

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

  Buffer中还有一个特殊的类型:MappedByteBuffer,用于实现内存映射。

  

  向Selector注册Channel,然后调用它的select()方法,这个方法会一直阻塞到通道中事件的发生,如新建连接、数据接收等。

 3.Channel示例

  我们将从文件中读取内容并打印,将使用Channel和Buffer。

  文件中的内容如下:

if you try,you may have a chance!
if you try,you may have a chance2! 朋友!
朋友!
朋友!
朋友!
朋友!
朋友!
朋友!

  读写的例子程序如下,其中还考虑了中文和读到半个中文字符的情况,只适用于文件编码格式为GBK的情形,这时一个汉字占2个字节。编码为UTF-8时,占用3个字节,需要重新考虑:

package nio;

import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; public class nio { public static void main(String[] args) throws Exception {
nio n = new nio();
n.readDataToBuffer();
} private void readDataToBuffer() throws Exception {
RandomAccessFile aFile = new RandomAccessFile("test.txt", "rw");
FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); byte [] bytes = new byte [48];
int index = 0;
buf.put("just for test ".getBytes(),3,6); //直接往buffer中放数据
int bytesRead = inChannel.read(buf); //数据通过chanel读到buffer中
int isChineseCharacterIndex = 0;
while (bytesRead != -1) { //缓冲区数据大小,-1表示没有数据
buf.flip(); //从写模式切换到读模式 while (buf.hasRemaining()) {
bytes[index] = buf.get();
if(bytes[index]<0){ //判断在最后读到半个汉字的情况,汉字占2个byte,这2个byte都是小于0的
isChineseCharacterIndex ++;
}
index++;
}
if(isChineseCharacterIndex%2 !=0){ //读到了半个汉字的情况,这半个汉字不显示,并将这半个汉字重新加到buffer中
System.out.println("Read " + (index-1));
System.out.println(new String(bytes,0,index-1));
buf.position(index-1);
buf.compact();
}else{
System.out.println("Read " + index);
System.out.println(new String(bytes,0,index));
buf.clear();
}
bytesRead = inChannel.read(buf);
index = 0;
isChineseCharacterIndex = 0;
//bytes = new byte [48]; read new data will override the old one
}
inChannel.position(inChannel.size()); //定位到文件末尾 //inChannel.write(ByteBuffer.wrap("\r\n朋友!".getBytes())); inChannel.close();
aFile.close();
}
}

  输出结果为:

Read 48
t for if you try,you may have a chance!
if you
Read 47
try,you may have a chance2! 朋友!
朋友!

Read 31
友!
朋友!
朋友!
朋友!
朋友!

  分析一下上面的代码, ByteBuffer.allocate(48)定义了一个大小为48B的缓冲区,inChannel.read(buf)是通过管道向缓冲区中写入数据,bytesRead != -1是指通过管道向缓冲区中写入的数据流不为空,下面的代码是循环的从缓冲区中读出所写入的数据。

  buf.flip() 是反转Buffer的读写状态,从之前的写状态变为读状态。

  Channel从文件中读数据到Buffer中时,我们也可以直接向Buffer中写入数据。flip切换成写状态时,写入到Channel中的数据也将写入到文件中。

  还需要注意的是:buf.position(index-1); buf.compact();作用是只保留最后一位字符(也就是半个中文字符)并移至Buffer顶端后清空Buffer。

  读完缓冲区中的数据后调用clear方法将清空缓冲区的数据。

 4.Buffer示例

  理解Buffer,首先需要理解position、limit和capacity在读模式和写模式中的不同作用。

  

  position在读模式表示当前写入数据的位置,从0开始,写模式下,position从0增加到capacity-1。读模式下position表示可以读取到的数据的位置,position将一直等于0。

  limit在写模式下等于capacity,表示最多可以写入多少数据。limit在读模式下,表示Buffer中一共有多少个数据。

  capacity在读模式和写模式下的作用相同,都表示最多可以写入多少数据。

  理解position、limit和capacity在读模式和写模式中的不同作用后,还需要了解Buffer中一些常用方法的使用,如下。

  clear(),只是把指针移到位置0,并没有真正清空数据。
  flip(), limit = position,指针指向0,常在读模式和写模式切换时使用。
  rewind(),指针指向0。
  compact(),压缩数据。比如当前capacity是6,当前指针指向2(即0,1的数据已经写出了,没用了),那么compact方法将把2,3,4,5的数据挪到0,1,2,3的位置。
  clear()和rewind()都是为新读入数据做好准备,区别在于clear会将capacity重置,而rewind不会重置capacity。

  关于clear、flip、rewind、compact和clear方法的使用,可以参考下面的例子:

package nio;

import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; public class nio2 {
public static void main(String[] args) throws Exception {
nio2 n = new nio2();
n.readDataToBuffer();
} private void readDataToBuffer() throws Exception {
RandomAccessFile aFile = new RandomAccessFile("test.txt", "rw");
FileChannel inChannel = aFile.getChannel(); //一般在把数据写入Buffer前调用clear,一般在从Buffer读出数据前调用flip,一般在把数据重写入Buffer前调用rewind
//Buffer and it's subclass is not thread safety
ByteBuffer buf = ByteBuffer.allocate(48); byte [] chars = new byte [48];
int index = 0;
int bytesRead = inChannel.read(buf);
System.out.println("============test rewind and clear method begin===============");
if (bytesRead != -1) {
System.out.println("Read " + bytesRead);
buf.flip(); //change read mode to write mode while (buf.hasRemaining()) {
chars[index] = buf.get();
index++;
}
System.out.println(new String(chars,0,index)); //中文的情况
}
buf.rewind(); //rewind method just reset the pointer position,not clear buffer,and we can get the data again
index = 0;
System.out.println("Read " + bytesRead);
//buf.flip(); //change write mode to read mode,and when write mode change to read mode,buffer will clear
//System.out.println("position: " + buf.position()+ ",capacity: " + buf.capacity() + ",limit: " + buf.limit()); //buf.clear(); //clear method just reset the pointer position,not clear buffer,and we can get the data again
while (buf.hasRemaining()) {
chars[index] = buf.get();
index++;
}
System.out.println(new String(chars));
System.out.println("============test rewind and clear method end==============="); //test compact method,
System.out.println("============test compact method begin===============");
buf.position(35); //assume has read 35 byte,set the pointer to 35
buf.compact(); //will not clear the buffer,but copy date 35-47 to 0-12,and the pointer to 13
System.out.println("position: " + buf.position()+ ",capacity: " + buf.capacity() + ",limit: " + buf.limit());
index = 0;
for(int i=0;i<13;i++){
chars[index] = buf.get(i);
index++;
} System.out.println("data before the pointer: " + new String(chars,0,index));
index = 0;
while (buf.hasRemaining()) {
chars[index] = buf.get();
index++;
}
System.out.println("data after the pointer: " + new String(chars,0,index));
System.out.println("============test compact method end==============="); //test mark and reset method
System.out.println("============test mark and reset method begin===============");
index = 0;
buf.rewind();
while (buf.hasRemaining()) {
chars[index] = buf.get();
index++;
if(index == 13){
buf.mark();
}
}
buf.reset(); System.out.println("position after reset: " + buf.position());
index = 0;
while (buf.hasRemaining()) {
chars[index] = buf.get();
index++;
}
System.out.println("data after reset: " + new String(chars,0,index));
System.out.println("============test mark and reset method end==============="); inChannel.close();
aFile.close();
}
}

  输出结果为:

        ============test rewind and clear method begin===============
Read 48
if you try,you may have a chance!
if you try,yo
Read 48
if you try,you may have a chance!
if you try,yo
============test rewind and clear method end===============
============test compact method begin===============
position: 13,capacity: 48,limit: 48
data before the pointer: if you try,yo
data after the pointer: u may have a chance!
if you try,yo
============test compact method end===============
============test mark and reset method begin===============
position after reset: 13
data after reset: u may have a chance!
if you try,yo
============test mark and reset method end===============

  写数据到Buffer有两种方式:

    从Channel写到Buffer。       int bytesRead = inChannel.read(buf);

    通过Buffer的put()方法写到Buffer里。     buf.put(127);

  从Buffer中读取数据有两种方式:

    从Buffer读取数据到Channel。        int bytesWritten = inChannel.write(buf);

    使用get()方法从Buffer中读取数据。         byte aByte = buf.get();

  还可以直接将一个Channel中的数据直接传输到另一个Channel中,可以见下面的例子: 

package nio;

import java.io.RandomAccessFile;
import java.nio.channels.FileChannel; public class nio3 { public static void main(String[] args) throws Exception{
RandomAccessFile fromFile = new RandomAccessFile("test.txt", "rw");
FileChannel fromChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel(); long position = 0;
long count = fromChannel.size(); toChannel.transferFrom(fromChannel,position, count);
//fromChannel.transferTo(position, count, toChannel); toChannel.close(); //channel关闭时会将数据写入
fromFile.close();
toFile.close();
} }

  运行代码后,将把test.txt中的内容拷贝到toFile.txt。

  

NIO基础篇(一)的更多相关文章

  1. NIO基础篇(三)

    NIO里对性能提升最显著的是内存映射(memory mapping),内存访问的速度往往比文件访问的速度快几个数量级. 在内存映射之前,还需要看NIO的一些其他的特性. 缓冲区分片 slice()方法 ...

  2. NIO基础篇(二)

    Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件.这样,一个单独的线程可以管理多个channel,从而管理多个网络连接. 传统的 ...

  3. 撸基础篇系列,JAVA的NIO部分

    前言:撸基础篇系列,避免每次都要从头开始看,写个自己的知识体系树 NIO 核心就是异步, 比如,复制文件,让操作系统去处理,等通知 BIO核心类 一,BIO NIO基本操作类 Bytebuffer 构 ...

  4. NIO相关基础篇一

    转载请注明原创出处,谢谢! 说在前面 NIO相关知识是很多后续的一些基础知识,所以今天这篇文章仅仅是简单介绍,后续会继续有一到二篇相关NIO内容. 什么是NIO Java NIO( New IO) 是 ...

  5. NIO相关基础篇二

    转载请注明原创出处,谢谢! 上篇NIO相关基础篇一,主要介绍了一些基本的概念以及缓冲区(Buffer)和通道(Channel),本篇继续NIO相关话题内容,主要就是文件锁.以及比较关键的Selecto ...

  6. NIO相关基础篇三

    转载请注明原创出处,谢谢! 说在前面 上篇NIO相关基础篇二,主要介绍了文件锁.以及比较关键的Selector,本篇继续NIO相关话题内容,主要谈谈一些Linux 网络 I/O模型.零拷贝等一些内容, ...

  7. Jenkins: 基础篇(环境配置)

    自动化领域比较有影响力的开源框架jenkins,确实比较强大,易用.很多公司将其用来做持续即成CI(continuous integration).为了拓展和强化自己的软件设计生态系统,也将很久前使用 ...

  8. 深入浅出微服务框架dubbo(一):基础篇

    一.基础篇 1.1 开篇说明 dubbo是一个分布式服务框架,致力于提供高性能透明化RPC远程调用方案,提供SOA服务治理解决方案.本文旨在将对dubbo的使用和学习总结起来,深入源码探究原理,以备今 ...

  9. 小白—职场之Java基础篇

    java基础篇 java基础 目录 1.java是一种什么语言,jdk,jre,jvm三者的区别 2.java 1.5之后的三大版本 3.java跨平台及其原理 4.java 语言的特点 5.什么是字 ...

随机推荐

  1. 51Nod 1046 A^B Mod C(日常复习快速幂)

    1046 A^B Mod C 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 给出3个正整数A B C,求A^B Mod C. 例如,3 5 8,3^5 Mod 8 = ...

  2. Spring框架学习笔记(8)——AspectJ实现AOP

    使用代理对象实现AOP虽然可以满足需求,但是较为复杂,而Spring提供一种简单的实现AOP的方法AspectJ 同样的计算器的DEMO 首先配置applicationContext.xml < ...

  3. SpringMVC整合Shiro权限框架

    尊重原创:http://blog.csdn.net/donggua3694857/article/details/52157313 最近在学习Shiro,首先非常感谢开涛大神的<跟我学Shiro ...

  4. 自制ZigBee协议分析仪

    关键词  ZigBee  Sniffer 协议  分析仪  自制  CC2530  CC2531 在开发ZigBee / Bluetooth的过程,难免会要用到Sniffer工具,Packet Sni ...

  5. 《你不知道的JavaScript上卷》知识点笔记

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px "PingFang SC" } p.p2 { margin: 0.0px ...

  6. POJ 2253 Frogger(Dijkstra变形——最短路径最大权值)

    题目链接: http://poj.org/problem?id=2253 Description Freddy Frog is sitting on a stone in the middle of ...

  7. vim&vi在编辑的时候突然卡死,不接收输入问题的解决

    多方查找无果,看了官方解释如下: "CTRL-S and CTRL-Q are called flow-control characters. They represent an antiq ...

  8. 用户使用VPS的12个常见问题

    1.VPS主机用户能否进行备份? VPS主机允许用户进行自主的备份,这个操作非常简单,是需要在用户控制面板点击备份按钮即可.同时还允许用户恢复到任何备份状态. 2.当某个VPS主机用户被攻击时,会不会 ...

  9. PHP $_SERVER['HTTP_REFERER'] 获取前一页面的 URL 地址

    转载:http://www.5idev.com/p-php_server_http_referer.shtml 使用 $_SERVER['HTTP_REFERER'] 将很容易得到链接到当前页面的前一 ...

  10. FORTH运算符

    body, table{font-family: 微软雅黑} table{border-collapse: collapse; border: solid gray; border-width: 2p ...