Java NIO 特性学习
Java NIO 包含几个核心的组件:
- Channels
- Buffer
- Selectors
Channels
可以理解为资源的一个流,通过这个流资源可以从Channel读取Data到一个Buffer中或者从一个Buffer中写入Data到Channel;
Channel Implementations
集中Jdk7常用的Channel上线
- FileChannel : 操作文件读取或者写入数据
- DatagramChannel : 从一个网络UDP连接中读取或写入数据
- SocketChannel : 从一个TCP网络连接中读取或写入数据
- ServerSocketChannel: 可以使用这个Channel来监听TCP连接,如同Web Server 当连接来时可以创建出一个SocketChannel
Base Channel Example
Read Data from Channel
RandomAccessFile
aFile =newRandomAccessFile("data/nio-data.txt","rw");FileChannel
inChannel = aFile.getChannel();ByteBuffer
buf =ByteBuffer.allocate(48);//write
data to bufferint
bytesRead = inChannel.read(buf);while(bytesRead!=-1){
System.out.println("Read
" + bytesRead);//swither
buffer type to read data from bufferbuf.flip();
while(buf.hasRemaining()){
System.out.print((char)
buf.get());}
buf.clear();
bytesRead=
inChannel.read(buf);}
aFile.close();
Buffer
Java NIO Buffers 和Channels配合使用,Read from channels into buffer ,writtern from buffers into channels
Buffer Usage
- 向buffer中写入数据
- 调用buffer.flip()方法
- 使用buffer.get()方法获取出数据
- 调用buffer.clear() 或者 buffer.compact()清理已经读取的数据
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt" , "rw");
FileChannel inChannel = aFile.getChannel();
//Create Buffer with capacity of 48 byte
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);
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);
}
Buffer Capacity , Position and Limit
Capacity
Buffer在创建的时候会分配规定大小的内存块,并且在buffer写入数据时只能写入这个固定大小的数据。如果Buffer已经放满数据,就需要先read 或者 clear 只能才能继续写入。
Position
Position 在读模式和模式下面含义不一样。Bufer init之后Position值为0,当数据写入one byte ,long etc Buffer Position相应的移动到下一个位置。并且Position<=Capacity-1
当调用filp()
从Buffer读取数据时会set position = 0
,position会随着读取过程向下移动;
Limit
在Write模式下,Limit等于Capacity的值,表示能写多少数据到Buffer中。
调用filp()
进入Reade模式会set limit = position
,表示最多能读的数据量。
常用的Buffer 实现
- ByteBuffer
- MappedByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
SomeMethods of Buffer
Allocating a Buffer
通过使用allocate()
来分配Buffer大小
ByteBuffer buf = ByteBuffer.allocate(48);
CharBuffer buf = CharBuffer.allocate(1024);
Write Data to a Buffer
向Buffer中写入数据有两种方法:
- 从Channel中读取数据写入Buffer
- 调用Buffer
put()
方法写入数据
int bytesRead = inChannel.read(buf); //read into buffer.
buf.put(127);
flip()
转换写状态到读状态,发生动作:set limit = position ; set position=0 ;
Reading Data from a Buffer
两种方法:
- 从Buffer中读取数据到Channel
- 读取Buffer中数据给自己,可以使用buffer自带方法
get()
etc
//read from buffer into channel.
int bytesWritten = inChannel.write(buf);
byte aByte = buf.get();
rewind()
set position=0
mark() 和 reset()
mark用于打点记录position为止,之后使用reset方法可以将position重置到mark记录的位置。
Scatter / Gather /Transfers
Channel 可以执行读取操作将数据读取到多个Buffer中。
Channel 可以执行写操作将多个Buffer的数据写入Channle中。
Channel 可以在Channle之间做转换包括
transferFrom() & transferTo
Scatter Reads :
例如我将html的header部分读取到第一个buffer中,将body部分读取到第二个Buffer中
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(buffers);
Gather Writes
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
//write data into buffers
ByteBuffer[] bufferArray = { header, body };
channel.write(buffers);
Transfers
TransferFrom()
RandomAccessFile fromFile = new RandomAccessFile("fromFile.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);
TransferTo()
RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel toChannel = toFile.getChannel();
long position = 0;
long count = fromChannel.size();
fromChannel.transferTo(position, count, toChannel);
Selector
Selector组件可以运行判断多个Channel,动态决定使用哪个Channel来执行Read 或者 Write操作。通过这个组件可以上线一个Thread 管理多个Channels 或者 多个网络连接Channel。
Create a Selector
通过使用Selector.open()
方法来创建一个Selector
Selector selector = Selector.open();
注册Channels到Selector上
为了能让Selector管理Channels需要调用Selector.register()
注册到Selector
channel.configureBlocking(false); //The Channel must be in non-blocking mode to be used with a Selector
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
注册Channel的时候需要制定Channel需要关心的事件,事件包括:
- Connect -> SelectionKey.OP_CONNECT
- Accept -> SelectionKey.OP_ACCEPT
- Read -> SelectionKey.OP_READ
- Write ->SelectionKey.OP_WRITE
如果注册多个事件可以使用
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
通过int interestSet = selectionKey.interestOps();
可以得到所有Selector’s的事件。
Simple Demo:
Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
while(true) {
int readyChannels = selector.select();
if(readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> 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();
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。