初探Java NIO
来源:互联网 发布:取消数据流量套餐 编辑:程序博客网 时间:2024/06/05 23:43
[NIO介绍]
java的nio是从jdk1.4起引入的.其目的只有一个:提高速度.实际上当我们使用”老”io包的时候,我们已经在跟nio打交道了.
老io包已经用nio重构过,即使不直接使用nio, 也一样能得到性能的提高.
之所以nio能有性能上的提高,是因为nio使用了跟操作系统io很相近的io处理方式:使用信道(channel)和缓冲(buffer)
我们不妨把buffer比作运煤的小车,把channel比成矿井.你不会直接下井把煤块手工地搬上来, 你要用矿车把煤矿一车一车拉出来. 同样,面对nio,你一般不会直接操作channel信道,而是读/写buffer.
需要注意的是, nio的底层特性是面向字节的,不是面向字符的.
nio有四个核心概念:
1. 缓冲区Buffer:在用户和信道之间进行信息传输。是大幅提高IO效率的精髓
2. 信道Channel:通往IO实体的连接
3. 字符集Charset:字符集以及对应的编码器(Encoder)解码器(decoder)一同行使字节向Unicode字符的转化。
4. 选择器Selector:和支持选择器的信道(Selectable Channel)一同构成多路复用的、非阻塞的IO信道(Multiplexed,non-blocking IO),典型例子就是Pipe.SinkChannel(管道信道的写终端),Pipe.SourceChannel(管道信道的读终端),
ServerSocketChannel(通往Socket监听者的信道),SocketChannel(通往Socket链路的信道)
[NIO和IO的相互转换]
由于nio底层是面向字节的,三个文件操作的老io类:FileInputStream, FileOutputStream,RandomAccessFile能够转换成FileChannel.
而所有的Reader和Writer,因其是面向字符的,不能转化成Channel. 但是有一个功能类:java.nio.channels.Channels提供了一些静态方法,能够把Reader/Writer转化成Channel.
下面给出Thinking in Java上面的一个例子,演示三种访问权限的Channel:writable,readable, writable/readable
import java.io.*;import java.nio.*;import java.nio.channels.*;public class GetChannel{private static final int BSIZE = 1024;public static void main(String[] args) throws IOException{// 新建并写入文件FileChannel fc = new FileOutputStream("data.txt").getChannel();fc.write(ByteBuffer.wrap("Some Text".getBytes()));fc.close();// 追加新内容fc = new RandomAccessFile("data.txt","rw").getChannel();fc.position(fc.size());//把文件指针放到文件尾. 实现"追加"fc.write(ByteBuffer.wrap("Some more".getBytes()));fc.close();// 读文件fc = new FileInputStream("data.txt").getChannel();ByteBuffer buff = ByteBuffer.allocate(BSIZE);fc.read(buff);buff.flip();while(buff.hasRemaining())System.out.print((char)buff.get());}}
[NIO中Buffer用法详解]
因为我们直接操作的不是Channel而是Buffer,所以Buffer的用法在NIO-IO转换中就显得至关重要。
为了高效利用Buffer内部的byte[]数组,Java语言设计者在Buffer里面定义了四个重要的索引变量(广义指针)mark、position、limit、capacity。Buffer的精髓就在这四个指针身上。
ByteBuffer的用法
成员方法/成员变量
类别
说明
static ByteBuffer
allocate(int size)
构造
申请一个size大小的byte[]数组,并且用ByteBuffer封装起来
例如:ByteBuffer bb = ByteBuffer.allocate(1024);
申请一个1024字节大小的ByteBuffer.
static ByteBuffer
wrap(byte[] src)
把已有的byte数组封装成ByteBuffer。类似于c++的move construct.
private int mark
索引
保存某个时刻的position指针的值,通过调用mark()实现;当mark被置为负值时,表示废弃标记。
private int position
A buffer's position is the index of the next element to be read or written.
位置指针。微观上,指向底层字节数组byte[] hb的某个索引位置;宏观上,是ByteBuffer的操作位置,如get()完成后,position指向当前(取出)元素的下一位,put()方法执行完成后,position指向当前(存入)元素的下一位;它是核心位置指针
private int limit
A buffer's limit is the index of the first element that should not be read or written.
也是位置指针,表示待操作数据的界限,它总是和读取或存入操作相关联,limit指针可以被 改变,可以认为limit<=capacity。
private int capacity
表示ByteBuffer的总长度/总容量,也即底层字节数组byte[] hb的容量,一般不可变,用于读取
注意:mark <= position <= limit <= capacity
public int capacity()
public int limit()
public int position()
getter
返回相应的capacity/limit/position值
public Buffer
clear()
clear() makes a buffer ready for a new sequence of channel-read or relative put operations: It sets the limit to the capacity and the position to zero.
-------------------------------------------
clear()为新的信道读取/推入缓冲做准备。
public Buffer
flip()
makes a buffer ready for a new sequence of channel-write or relative get operations: It sets the limit to the current position and then sets the position to zero.
-------------------------------------------
socket的write操作完成后,若需要read刚才write到的数据,则需要在read执行前执行flip(),以重置操作位置指针,保存操作数据的界限,保证read数据准确。
limit=position; position = 0; mark=-1;
public Buffer
rewind()
makes a buffer ready for re-reading the data that it already contains: It leaves the limit unchanged and sets the position to zero.
------------------------------------------
和flip()相比,只是没有改变limit
public Buffer
reset()
Resets this buffer's position to the previously-marked position.
把position重新设置为之前标志过的mark
public Buffer
limit(int lim)
setter
把limit设置成指定值。limit必须是[0,capacity]区间内的整数。
public void mark()
把mark设置为position。作用是保存某时刻的mark值
public Buffer
position(int pos)
把position设置成指定值。position必须是[0,limit]之间的整数
public int
remaining()
getter
返回(limit-position)
/** * Powered By * Thinking in Java 4th edition */import java.nio.*;public class UsingBuffers{private static void symmetricScramble (CharBuffer buffer){while(buffer.hasRemaining()){buffer.mark();char c1 = buffer.get();char c2 = buffer.get();buffer.reset();buffer.put(c2).put(c1);}}public static void main(String[] args){char[] data = "UsingBuffers".toCharArray();ByteBuffer bb = ByteBuffer.allocate(data.length*2);CharBuffer cb = bb.asCharBuffer();cb.put(data);print(cb.rewind().array());symmetricScramble(cb);print(cb.rewind().array());symmetricScramble(cb);print(cb.rewind().array());}}
这是第一次进入symmetricScramble时缓冲区的状态
第一次执行symmetricScramble的时候,while循环在position==limit的时候终止。当你调用get、put之类的方法时,缓冲区的position指针会在方法返回前移动后移。当然你也可以指定get和put的操作位置。
进入while循环的时候,mark()将mark设置为position。此时的buffer状态是
两次get()调用之后:
两次put()之后
执行下一轮循环。
这个过程会一直重复直到position到达limit。结果如下
注意到每次打印的时候都要rewind一下。因为如果想要打印整个buffer的内容,你需要把position设置到数组的起点。
rewind示意图:
- java.nio包初探
- 初探Java NIO
- Java NIO初探
- java nio初探
- Java NIO初探(一)
- java-非阻塞异步通信-NIO初探
- NIO初探
- NIO初探
- Java NIO和Netty框架(一)初探NIO
- Java Nio初探及普通io性能比较
- NIO机制初探
- Nio初探与netty实战
- Java NIO: NIO概述
- Java NIO:NIO概述
- Java NIO:NIO概述
- Java NIO:NIO概述
- Java NIO:NIO概述
- Java NIO:NIO概述
- VxWorks资料
- 安卓第二次实验单:openGL资料查阅
- spring mvc 下的mysql+mybatis批量更新
- Java性能调优工具
- 继承(Inheritance)
- 初探Java NIO
- c++中的.hpp文件
- 展示一下香蕉派路由Android系统
- stm32对nrf24l01无法操作
- 嵌入式 Ubuntu下设置VPN连接
- 算法学习笔记(八) 动态规划的一般求解方法
- 完整详解GCD系列(一)dispatch_async;dispatch_sync;dispatch_async_f;dispatch_sync_f
- Leetcode: Longest Substring Without Repeating Characters
- Excel 表内金额小写转大写函数