我们解读一下 DirectByteBuffer的父类 MappedByteBuffer
解释: 一个直接的缓冲区,一个文件内存的映射区域
是通过FileChannel#map来实现,是允许java程序直接从内存访问的特殊的文件,将整个文件 或者部分文件 映射到内存当中,由操作系统将内存的修改写入到文件中,我们的应用程序只需操作内存数据 进行迅速的IO操作,用于内存映射文件的内存本身 是在java堆的外面也是就堆外内存。
/**
* A direct byte buffer whose content is a memory-mapped region of a file.
*
* <p> Mapped byte buffers are created via the {@link
* java.nio.channels.FileChannel#map FileChannel.map} method. This class
* extends the {@link ByteBuffer} class with operations that are specific to
* memory-mapped file regions.
*
* <p> A mapped byte buffer and the file mapping that it represents remain
* valid until the buffer itself is garbage-collected.
*
* <p> The content of a mapped byte buffer can change at any time, for example
* if the content of the corresponding region of the mapped file is changed by
* this program or another. Whether or not such changes occur, and when they
* occur, is operating-system dependent and therefore unspecified.
*
* <a name="inaccess"></a><p> All or part of a mapped byte buffer may become
* inaccessible at any time, for example if the mapped file is truncated. An
* attempt to access an inaccessible region of a mapped byte buffer will not
* change the buffer's content and will cause an unspecified exception to be
* thrown either at the time of the access or at some later time. It is
* therefore strongly recommended that appropriate precautions be taken to
* avoid the manipulation of a mapped file by this program, or by a
* concurrently running program, except to read or write the file's content.
*
* <p> Mapped byte buffers otherwise behave no differently than ordinary direct
* byte buffers. </p>
*
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
*/
public abstract class MappedByteBuffer
extends ByteBuffer
举个栗子:
public class NioTest7 {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("NioTest9.txt","rw");
FileChannel fileChannel = randomAccessFile.getChannel();
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
//只需操作内存 不需要写入文件 怎么把数据写入文件当中 都是由操作系统来完成 不需要我们去管
mappedByteBuffer.put(0,(byte)'a');
mappedByteBuffer.put(3,(byte)'b');
randomAccessFile.close();
}
}
下面我们来说 文件锁
public class NioTest8
{
public static void main(String[] args) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile("NioTest9.txt","rw");
FileChannel fileChannel = randomAccessFile.getChannel();
//1.位置 2多长 3 ture是否是共享锁 false 排他锁
FileLock fileLock = fileChannel.lock(3,6,true);
// 是否是有效的
System.out.println("valid :"+fileLock.isValid());
// 锁的类型是否是共享锁
System.out.println("lock type :" +fileLock.isShared());
fileLock.release();
randomAccessFile.close();
}
}
关于Buffer的Scattering与Gathering:
Java NIO开始支持scatter/gather,scatter/gather用于描述从Channel(译者注:Channel在中文经常翻译为通道)中读取或者写入到Channel的操作。
分散(scatter)从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据“分散(scatter)”到多个Buffer中。
聚集(gather)写入Channel是指在写操作时将多个buffer的数据写入同一个Channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。
scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体.
之前我们read或write都是操作一个buffer 装满之后重新定义position位置重新在往里面读或写,而Scattering 不仅可以传一个buffer 还可以传buffer数组 比如我们一个buffer[0] 长度2 buffer[1] 长度5 buffer[2] 长度9,只有把第一个读满 在去读第二个,而Gathering 写也可以传buffer数组。
那么什么时候会这么使用呢:
比如我们在网络操作的时候 自定义协议 第一个传递过来的请求数据 第一个 buffer[1] 长度5 buffer[2] 长度9 作为Header buffer[3] 作为 Body
举个栗子:
package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
/**
* 关于Buffer的Scattering与Gathering
*/
public class NioTest9 {
public static void main(String[] args) throws IOException{
ServerSocketChannel serverSocketChannel =ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(8899);
serverSocketChannel.socket().bind(address);
int messageLenth = 2 + 3 +4;
ByteBuffer[] buffers = new ByteBuffer[3];
buffers[0] = ByteBuffer.allocate(2);
buffers[1] = ByteBuffer.allocate(3);
buffers[2] = ByteBuffer.allocate(4);
SocketChannel socketChannel = serverSocketChannel.accept();
while (true){
int byteRead = 0;
// 如果读到的字节数小于总的 继续读
while (byteRead <messageLenth){
long r = socketChannel.read(buffers);
byteRead += r;
System.out.println("byteRead :"+byteRead);
Arrays.asList(buffers).stream().map(buffer -> "position:"+ buffer.position()+",limit"+buffer.limit()).forEach(System.out::println);
}
Arrays.asList(buffers).forEach(buffer ->{
buffer.flip();
});
long bytesWritee =0;
while (bytesWritee < messageLenth){
long r = socketChannel.write(buffers);
bytesWritee +=r;
}
Arrays.asList(buffers).forEach(buffer ->{
buffer.clear();
});
System.out.println("byteRead :"+byteRead+ ",bytesWritee:"+bytesWritee+",messageLenth:"+messageLenth);
}
}
}
我们使用telnet localhost 8899
输入hellowor+回车 正好是9个字节
byteRead :9
position:2,limit2
position:3,limit3
position:4,limit4
byteRead :9,bytesWritee:9,messageLenth:9
如果输入 hello+回车 是6个字节
byteRead :6
position:2,limit2
position:3,limit3
position:1,limit4