Buffer数据结构和new IO的Memory-mapped files

一、Buffer类

  java.nio.Buffer这个类是用来干什么的?有怎样的结构?

  "Core Java"中是这样定义的“A buffer is array of values of the same type”。所以,我们可以感性的认识到:buffer就像数组一样,存放的是相同类型的数据。还有一个重要的事情就是:Buffer是一种随机存储类型的数据结构,就像普通数组一样(用下标的方式)能够用索引号定位到buffer中的任何一个位置的数据上)。

  Buffer类是一个抽象类,其子类有(注意:StringBuffer类和这里的buffer没有什么联系):

Buffer类中的属性和方法用于管理和控制buffer的状态,其子类则有get或put方法用于“读出”或“写入”不同类型的数据;我们还应该注意到:ByteBuffer、CharBuffer等易燃是一个抽象类。所以,我们是不能够通过new的方式得到一个ByteBuffer或CharBuffer。现在,万一我们想得到一个CharBuffer怎么办呢(ByteBuffer可以用同样的方式得到)?

答案:可以利用CharBuffer的allocate(int capacity)方法,或者是对现有的char[] array进行包装的wrap方法,如下:

在我们实际的编程中使用最多的是ByteBuffer和CharBuffer,如下图所示,Buffer具有以下4个属性:

①、capacity,一个Buffer建立以后,它就固定不变了;

②、position,它指示了下一个要读或写数据的位置;

③、limit,超过limit位置的数据是没有意义的;

④、mark,它标记某个重要的位置,用于后面能够返回到该位置进行重新读或写;

它们四个的关系是:

0≤mark≤position≤limit≤capacity

Buffer最重要的一个作用就是:循环的用于“先写,后读”。下面是一个先写后读的过程:

①、最开始的时候,position=0 && limit=capacity;

②、调用put方法向buffer中写数据,当数据写完了或者是position到达了capacity的位置,下面就开始从buffer中读取数据;

③、从“写”状态向“读”状态转变,需要调用flip方法。其作用就是:先让limit等于当前的position,然后将position设置为0;

④、读取数据的时候,只要remaining方法(limit-position)返回一个正数,那么我们就可以持续的调用get方法从buffer中读取数据;

⑤、当读取数据过程完毕了,我们可以调用clear方法,将buffer从“读”状态转换为“写”状态,进入下一个“先读,后写”循环;

⑥、clear方法时设置position=0 && limit=capacity;

⑦、如果想重新读取buffer,可以调用其rewind或者是mark/reset方法,API中有详细介绍;

API:java.nio.Buffer

①、Buffer clear()

设置buffer进入到写状态,设置position=0 && limit=capacity;

②、Buffer flip()

设置buffer进入到读状态,设置limit等于当前的position && position=0;

③、Buffer rewind()  //rewind可以翻译为“倒回”,“倒带”等

准备重新读取buffer中相同的数据,设置position=0 && limit保持不变;

④、Buffer mark()

将当前的position设置为mark,其可以配合reset()方法实现buffer的重读/写;

⑤、Buffer reset()

设置position=mark,从而可以从mark位置重新开始读或写;

⑥、int remaining()

返回buffer中“可读”数据的个数,或者是还可以“写入”多少个新的数据,返回limit-position;

⑦、int position()   返回当前position的值;

⑧、int capacity()   返回buffer的capacity的值;

二、nio的文件映射

下面我们看java.nio包相对于旧的IO而言有哪些增强的新特性:

nio主要是支持一下四个增强的特性:

①、字符集的编码和解码;

②、非阻塞IO(nonblocking I/O);

③、Memmory-mapped files;

④、文件锁;

  对字符集的编码和解码可以单独拿出来讲。非阻塞IO主要用在网络通信中。文件锁是一个复杂却不怎么靠得住的东西(依赖于具体操作系统对锁的支持),在并发的情况下,通常可以借助于数据库的锁机制,将文件存入数据库中即可实现文件的同步。

  这里主要总结Memmory-mapped files。

  大多数操作系统可以利用虚拟内存(virtual memory)的优势,将整个文件或者是文件的一部分映射到内存中。然后,我们就可以像内存数组一样访问映射文件了(主要是其随机访问特性),这样会比传统的文件操作(RandomAccessFile)要快很多。

  【讨论:对文件的操作有大体的三种方式File流、RandomAccessFile、Memmory-mapped files,但是各有特色:①文件流和缓冲流结合起来会很快,但是不具有随机访问特性;②、RandomAccessFile有随机访问特性,但是它效率十分低下;③、Memmory-mapped files具有随机访问特性,其效率甚至要比缓冲流还高,它主要是用于对“大文件”的操作上】

  文件的映射操作比较简单,依据下面的步骤即可:

  ①、从文件中获取到一个channel。其中channel是磁盘文件的一个抽象,通过它能够获取到操作系统的一些特性,比如:内存映射、文件锁、文件间的快速数据传递。在jak1.4中已经重写了FileInputStream、FileOutputStream和RandomAccessFile类,为它们添加了getChannel方法。所以,我们可以通过调用getChannel方法获取到磁盘文件的channel。如下:

FileInputStream in = new FileInputStream(...);
FileChannel channel = in.getChannel();

  ②、从channel中获取到MappedByteBuffer。我们可以通过调用Channel类的map方法进行文件的映射,此方法会返回一个MappedByteBuffer对象。在map方法中我们可以指定文件映射的范围(全部或者是部分),还可以指定映射的模式,共支持三种模式:

  ----FileChannel.MapMode.READ_ONLY:只能从buffer中读取数据,不能像buffer中写入数据。当调用写方法的时候会抛出一个ReadOnlyBufferException异常。

  ----FileChannel.MapMode.READ_WRITE:buffer是可写的,同时buffer中改变的数据会在某个时候写回到文件中。注意,其它也映射了该文件的程序并不能马上感知到这一改变(所以,有了文件同步锁机制)。

  ----FileChannel.MapMode.PRIVATE:buffer是可写的,但是任何改变都不会写回到文件中去。

  ③、一旦我们得到了一个buffer,那么我们就可以调用Buffer或者是其子类的方法对buffer进行数据的访问。注意,Buffer同时支持顺序访问和随机访问两种方式。比如,下面的两个例子:

//使用顺序访问buffer
while(buffer.hasRemaining()){
     byte b = buffer.get();
     ...
}

//使用随机访问buffer
for(int i = 0; i < buffer.limit(); i++){
     byte b = buffer.get(i);   //Buffer这个抽象类没有get和put方法,它只负责管理和控制buffer的状态
     ...
}

buffer.order(ByteOrder.LITTLE_ENDIAN);  指定小端存储

ByteOrder b = buffer.order();   找出当前buffer中存放byte的模式

下面的一个例子中,分别使用了FileInputStream,BufferedInputStream,RandomAccessFile和MappedFile来读取rt.jar文件(59.8MB),并计算器CRC32值。我们可以对比四种处理方式的效率,得到一个感性的认识:

package nio;

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.Scanner;
import java.util.zip.CRC32;

public class NIOtest {

    public static long checksumInputStream(String filename) throws Exception{

        CRC32 crc32;
        InputStream in = null;
        try {
            crc32 = new CRC32();
            in = new FileInputStream(filename);

            int c;
            while((c = in.read()) != -1){
                crc32.update(c);
            }
        } finally{
            if(in != null)
                in.close();
        }

        return crc32.getValue();
    }

    public static long checksumBufferedInputStream(String filename) throws Exception{

        CRC32 crc32 = new CRC32();
        InputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(filename));
            int b;
            while((b = in.read()) != -1){
                crc32.update(b);
            }
        } finally{
            if(in != null)
                in.close();
        }

        return crc32.getValue();
    }

    public static long checksumRandomAccessFile(String filename) throws Exception{

        RandomAccessFile file = null;
        CRC32 crc = new CRC32();
        try {
            file = new RandomAccessFile(filename, "r");//只读模式
            long length = file.length();
            for(int i = 0; i < length; i++){
                file.seek(i);
                int b = file.read();
                crc.update(b);
            }
        } finally{
            if(file != null)
                file.close();
        }

        return crc.getValue();
    }

    public static long checksumMappedFile(String filename) throws Exception{

        CRC32 crc = new CRC32();
        FileInputStream in = null;
        FileChannel channel = null;
        try {
            in = new FileInputStream(filename);
            channel = in.getChannel();
            int size = (int) channel.size();
            MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, size);
            for(int p = 0; p < size; p++){
                int b = buffer.get(p);
                crc.update(b);
            }
        } finally{
            if(in != null)
                in.close();
        }

        return crc.getValue();
    }

    public static void main(String[] args) throws Exception {
        System.out.println("输入测试文件路径:");
        InputStream in = System.in;
        Scanner scanner = new Scanner(in);
        String filename = scanner.nextLine();
        System.out.println("开始计算...");
        System.out.println();

        long start = System.currentTimeMillis();
        long crcValue = checksumInputStream(filename);
        long end = System.currentTimeMillis();
        System.out.println("---InputStream----> " + (end - start ) + " 毫秒。 crc32 值: " + Long.toHexString(crcValue));

        start = System.currentTimeMillis();
        crcValue = checksumBufferedInputStream(filename);
        end = System.currentTimeMillis();
        System.out.println("---BufferedInputStream----> " + (end - start ) + " 毫秒。 crc32 值: " + Long.toHexString(crcValue));

        start = System.currentTimeMillis();
        crcValue = checksumRandomAccessFile(filename);
        end = System.currentTimeMillis();
        System.out.println("---RandomAccessFile----> " + (end - start ) + " 毫秒。 crc32 值: " + Long.toHexString(crcValue));

        start = System.currentTimeMillis();
        crcValue = checksumMappedFile(filename);
        end = System.currentTimeMillis();
        System.out.println("---MappedFile----> " + (end - start ) + " 毫秒。 crc32 值: " + Long.toHexString(crcValue));

        if(scanner != null)
            scanner.close();
    }

}

执行结果:

输入测试文件路径:
E:\Java\jdk1.8.0_25\jre\lib\rt.jar
开始计算...

---InputStream----> 222721 毫秒。 crc32 值: 2a57ac2
---BufferedInputStream----> 3020 毫秒。 crc32 值: 2a57ac2
---RandomAccessFile----> 377263 毫秒。 crc32 值: 2a57ac2
---MappedFile----> 4323 毫秒。 crc32 值: 2a57ac2
时间: 2024-10-08 12:34:21

Buffer数据结构和new IO的Memory-mapped files的相关文章

C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转

原文:C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). 内存映射文件对于托管世界的开发人员来说似乎很陌生,但它确实已经是很远古的技术了,而且在操作系统中地位相当.实际上,任何想要共享数据的通信模型都会在幕后使用它. 内存映射文件究竟是个什么?内存映射文件允许你保留一块地址空间,然后将该物理存储映射到这块内存空间中进行操作.物理存储是文件管理,而内存

虚拟内存(VirtualAlloc),堆(HeapAlloc/malloc/new)和Memory Mapped File

内存管理有三种方式: 1. 虚拟内存,VirtualAlloc之类的函数 2. 堆,Heapxxx函数,malloc,new等 3. 内存映射文件,Memory Mapped File 非常多人都会困惑,可是看以下的图片就会比較明确了.这个图片从MSDN上拷来. 堆和虚拟内存.从上面的图片就能够看出,事实上所谓的堆,也就是在虚拟内存上抽象出来的. 假设直接用Virtualxxx系列函数,是有一些限制的,比方每次仅仅能分配页大小倍数的内存.内存地址也必须对齐什么的.新手非常难用.正由于如此,才出现

C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped

节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). 内存映射文件对于托管世界的开发人员来说似乎很陌生,但它确实已经是很远古的技术了,而且在操作系统中地位相当.实际上,任何想要共享数据的通信模型都会在幕后使用它. 内存映射文件究竟是个什么?内存映射文件允许你保留一块地址空间,然后将该物理存储映射到这块内存空间中进行操作.物理存储是文件管理,而内存映射文件是操作系统级内存管理. 优势:     1.访问磁盘文件上的数据不需执行I/O操作和缓存

Unix文件系统学习笔记之一 : IO系统调用

1. IO system call six basic operation: open create close write read lseek ioctl unlink 更高级的IO system call: pread pwrite readv writev preadv pwritev aio_read aio_write lio_listio unlink flush dup2 2.  文件中data和meta-data的cache属性: O_SYNC: write operation

dirty memory

关于各种内存的解释,来源于stackoverflow Wired : This refers to kernel code and such. Memory that should not   ever be moved out of the RAM. Also know as resident memory. Shared : Memory that is shared between two or more processes. Both   processes would show thi

Shared Memory共享内存----kithara RTS

翻译注:共享内存是程序之间进行数据交互的最基本的方式,而由于windows和kithara rts本身为两个独立的系统,为了能够使两个不同系统之上的程序进行通信,那么就必须开辟一块内存区域用于数据共享.本文是对kithara rts官方原文进行的翻译,加入了本人的一些使用经验. Shared Memory(共享内存) What you need to know about Shared Memory(共享内存基本知识) In 32-bit and64-bit Windows operating

Go语言(golang)开源项目大全

转http://www.open-open.com/lib/view/open1396063913278.html内容目录Astronomy构建工具缓存云计算命令行选项解析器命令行工具压缩配置文件解析器控制台用户界面加密数据处理数据结构数据库和存储开发工具分布式/网格计算文档编辑器Encodings and Character SetsGamesGISGo ImplementationsGraphics and AudioGUIs and Widget ToolkitsHardwareLangu

Boost 1.61.0 Library Documentation

http://www.boost.org/doc/libs/1_61_0/ Boost 1.61.0 Library Documentation Accumulators Framework for incremental calculation, and collection of statistical accumulators. Author(s): Eric Niebler First Release: 1.36.0 Standard: Categories: Math and nume

[转]Go语言(golang)开源项目大全

内容目录 Astronomy 构建工具 缓存 云计算 命令行选项解析器 命令行工具 压缩 配置文件解析器 控制台用户界面 加密 数据处理 数据结构 数据库和存储 开发工具 分布式/网格计算 文档 编辑器 Encodings and Character Sets Games GIS Go Implementations Graphics and Audio GUIs and Widget Toolkits Hardware Language and Linguistics 日志 机器学习 Math