IO 和 NIO的区别,NIO优点

Java NIO提供了与标准IO不同的IO工作方式: 

Channels and Buffers(通道和缓冲区):标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

Asynchronous IO(异步IO):Java NIO可以让你异步的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。

Selectors(选择器):Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。

阻塞IO和非阻塞IO

Java IO流都是阻塞的,这意味着,当一条线程执行read()或者write()方法时,这条线程会一直阻塞直到读取到了一些数据或者要写出去的数据已经全部写出,在这期间这条线程不能做任何其他的事情。

java NIO的非阻塞模式(Java NIO有阻塞模式和非阻塞模式,阻塞模式的NIO除了使用Buffer存储数据外和IO基本没有区别)允许一条线程从channel中读取数据,通过返回值来判断buffer中是否有数据,如果没有数据,NIO不会阻塞,因为不阻塞这条线程就可以去做其他的事情,过一段时间再回来判断一下有没有数据。NIO的写也是一样的,一条线程将buffer中的数据写入channel,它不会等待数据全部写完才会返回,而是调用完write()方法就会继续向下执行

选择器(Selectors)

 Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

面向流与面向缓冲

Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

Java NIO 由以下几个核心部分组成:

Channels/Buffers/Selectors

 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。

Channel

Channel的实现:(涵盖了UDP和TCP网络IO以及文件IO)

FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel

读数据:int bytesRead = inChannel.read(buf);

写数据:int bytesWritten = inChannel.write(buf);  

还有部分的使用,如配置Channel为阻塞或者非阻塞模式,以及如何注册到Selector上面去,参考Selector部分;

Buffer

Buffer实现:(byte,char,short,int,long,float,double)

ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer

Buffer使用:

读数据:

flip()方法:将Buffer从写模式切换到读模式;调用flip()方法会将position设回0,并将limit设置成之前position的值。

(char) buf.get():读取数据

rewind():将position设回0,所以你可以重读Buffer中的所有数据;limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)。

mark()方法:可以标记Buffer中的一个特定position。

reset()方法:恢复到Buffer.mark()标记时的position。

一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。

clear()方法:清空整个缓冲区;position将被设回0,limit被设置成 capacity的值。

compact()方法:只会清除已经读过的数据;任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面;将position设到最后一个未读元素正后面,limit被设置成 capacity的值。

写数据

buf.put(127);  

Buffer的三个属性:

capacity:含义与模式无关;Buffer的一个固定的大小值;Buffer满了需要将其清空才能再写;

ByteBuffer.allocate(48);该buffer的capacity为48byte

CharBuffer.allocate(1024);该buffer的capacity为1024个char 

position:含义取决于Buffer处在读模式还是写模式(初始值为0,写或者读操作的当前位置)

写数据时,初始的position值为0;其值最大可为capacity-1

将Buffer从写模式切换到读模式,position会被重置为0

limit:含义取决于Buffer处在读模式还是写模式(写limit=capacity;读limit等于最多可以读取到的数据)

写模式下,limit等于Buffer的capacity

切换Buffer到读模式时, limit表示你最多能读到多少数据;

Selector

Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。

要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。 

创建:

Selector selector = Selector.open();  

注册通道:

channel.configureBlocking(false);  

/*与Selector一起使用时,Channel必须处于非阻塞模式,这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式(而套接字通道都可以)*/

SelectionKey key = channel.register(selector, Selectionkey.OP_READ); 

/*第二个参数表明Selector监听Channel时对什么事件感兴趣(SelectionKey.OP_CONNECT  SelectionKey.OP_ACCEPT  SelectionKey.OP_READ SelectionKey.OP_WRITE),可以用或操作符将多个兴趣组合一起*/

SelectionKey

包含了interest集合 、ready集合 、Channel 、Selector 、附加的对象(可选)

int interestSet = key.interestOps();可以进行类似interestSet & SelectionKey.OP_CONNECT的判断

使用:

select():阻塞到至少有一个通道在你注册的事件上就绪了

selectNow():不会阻塞,不管什么通道就绪都立刻返回

selectedKeys():访问“已选择键集(selected key set)”中的就绪通道

close():使用完selector需要用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效

Set selectedKeys = selector.selectedKeys(); 

 Iterator keyIterator = selectedKeys.iterator();  

while(keyIterator.hasNext()) {      

    SelectionKey key = keyIterator.next();     

     if(key.isAcceptable()) {         

         // a connection was accepted by a ServerSocketChannel.     

     } else if (key.isConnectable()) {          

        // a connection was established with a remote server.      

    } else if (key.isReadable()) {          

        // a channel is ready for reading      

    } else if (key.isWritable()) {          

        // a channel is ready for writing     

     }    

    keyIterator.remove();//注意这里必须手动remove;表明该selectkey我已经处理过了;

}



Java测试关键代码

RandomAccessFile aFile =newRandomAccessFile("data/nio-data.txt","rw");

FileChannel inChannel = aFile.getChannel();  //从一个InputStream outputstream中获取channel

//create buffer with capacity of 48 bytes

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);//read into buffer.

while(bytesRead != -1) {

    buf.flip();//make buffer ready for read

    while(buf.hasRemaining()){

        System.out.print((char) buf.get());// read 1 byte at a time

  }  

    buf.clear();//make buffer ready for writing

    bytesRead = inChannel.read(buf);  

}  

aFile.close();  

文件通道

RandomAccessFile aFile =newRandomAccessFile("data/nio-data.txt","rw");

FileChannel inChannel = aFile.getChannel();  

读数据

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);

写数据

String newData ="New String to write to file..."+ System.currentTimeMillis(); 

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();  

buf.put(newData.getBytes());  

buf.flip();  

while(buf.hasRemaining()) {

    channel.write(buf);  

Socket 通道

SocketChannel socketChannel = SocketChannel.open();  

socketChannel.connect(newInetSocketAddress("http://jenkov.com",80));

读数据

ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = socketChannel.read(buf);

写数据

String newData ="New String to write to file..."+ System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();  

buf.put(newData.getBytes());  

buf.flip();  

while(buf.hasRemaining()) {

    socketChannel.write(buf);  

}  

ServerSocket 通道

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();  

serverSocketChannel.socket().bind(newInetSocketAddress(9999));

while(true){

    SocketChannel socketChannel =  

            serverSocketChannel.accept();  

//do something with socketChannel...

Datagram通道(channel 的读写操作与前面的有差异)

DatagramChannel channel = DatagramChannel.open();  

channel.socket().bind(newInetSocketAddress(9999));

读数据

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();  

channel.receive(buf);

/*receive()方法会将接收到的数据包内容复制到指定的Buffer. 如果Buffer容不下收到的数据,多出的数据将被丢弃。 */

写数据

String newData ="New String to write to file..."+ System.currentTimeMillis();

ByteBuffer buf = ByteBuffer.allocate(48);

buf.clear();  

buf.put(newData.getBytes());  

buf.flip();  

int bytesSent = channel.send(buf,newInetSocketAddress("jenkov.com",80));


本文部分转自:

Java NIO 与 IO之间的区别

Java NIO:IO与NIO的区别


个人公号:【排骨肉段】,可以关注一下。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容