🎈博客主页:🌈我的主页🌈
🎈欢迎点赞 👍 收藏 🌟留言 📝 欢迎讨论!👏
🎈本文由 【泠青沼~】 原创,首发于 CSDN🚩🚩🚩
🎈由于博主是在学小白一枚,难免会有错误,有任何问题欢迎评论区留言指出,感激不尽!🌠个人主页
目录
- 🌟 一、核心要点
- 🌟🌟 1.1、MappedByteBuffer
- 🌟🌟1.2、 FileChannel
- 🌟🌟1.3、 RandomAccessFile
- 🌟 二、Java类的实现
- 🌟🌟 2.1、写进程(加锁)
- 🌟🌟 2.2、读进程(加锁)
- 🌟🌟 2.3、运行读写进程
- 🌟🌟 2.4、写进程(不加锁)
- 🌟🌟 2.4、读进程(不加锁)
🌟 一、核心要点
🌟🌟 1.1、MappedByteBuffer
Java IO
操作的BufferedReader
、 BufferedInputStream
等相信大家都很熟悉,不过在 Java NIO
中引入了一种基于MappedByteBuffer
操作大文件的方式,其读写性能极高。
MappedByteBuffer
为共享内存缓冲区,实际上是一个磁盘文件的内存映射,实现内存与文件的同步变化,可有效地保证共享内存的实现。
🌟🌟1.2、 FileChannel
FileChannel
是将共享内存和磁盘文件建立联系的文件通道类。FileChannel
类的加入是JDK
为了统一对外设备(文件、网络接口等)的访问方法,并加强了多线程对同一文件进行存取的安全性。我们在这里用它来建立共享内存和磁盘文件间的一个通道。
🌟🌟1.3、 RandomAccessFile
RandomAccessFile
是Java IO
体系中功能最丰富的文件内容访问类,它提供很多方法来操作文件,包括读写支持,与普通的IO
流相比,它最大的特别之处就是支持任意访问的方式,程序可以直接跳到任意地方来读写数据。
举个例子:
如果我们要向已存在的大小为 1G 的 txt 文本里末尾追加一行文字,内容如下“ 你好,我是小明”。其实直接使用Java 中的流读取 txt 文本里所有的数据转成字符串后,然后拼接“你好,我是小明”,又写回文本即可。
但如果需求改了,我们要想向大小为 8G 的 txt 文本里追加数据。如果我们电脑的内存只有 4G ,强制读取所有的数据并追加,将会报内存溢出的异常。显然,上面的方法不再合适。
如果我们使用 JAVA IO 体系中的 RandomAccessFile类来完成的话,可以实现零内存追加。其实,这就是支持任意位置读写类的强大之处。
🌟 二、Java类的实现
🌟🌟 2.1、写进程(加锁)
/**
* create by dong on 2023/5/8
*/
public class NIOWriteLock {
private static RandomAccessFile raf;
public static void main(String[] args) throws Exception {
//获取随机存取文件对象,建立文件和内存的映射,即时双向同步
raf = new RandomAccessFile("E:/temp/source.dat", "rw");
FileChannel fc = raf.getChannel(); //获取文件通道
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024); //获取共享内存缓冲区
FileLock flock=null;
for(int i=65;i<91;i++){
//阻塞独占锁,当文件锁不可用时,当前进程会被挂起
flock=fc.lock();
System.out.println(System.currentTimeMillis() + ":write:" + (char)i);
mbb.put(i-65,(byte)i); //从文件第一个字节位置开始写入数据
flock.release(); //释放锁
Thread.sleep(1000);
}
}
}
🌟🌟 2.2、读进程(加锁)
public class NIOReadLock {
private static RandomAccessFile raf;
public static void main(String[] args) throws Exception {
raf = new RandomAccessFile("D:/tmp/data.dat", "rw");
FileChannel fc = raf.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024);
FileLock flock=null;
for(int i=0;i<26;i++){
flock=fc.lock(); //上锁
System.out.println( System.currentTimeMillis() + ":read:" + (char)mbb.get(i));
flock.release(); //释放锁
Thread.sleep(1000);
}
}
}
🌟🌟 2.3、运行读写进程
因为我们采用了文件锁方式来规范读写操作,该方法在读操作和写操作之前都采用加锁来保证数据安全。
🌟🌟 2.4、写进程(不加锁)
public class NIOWrite {
private static RandomAccessFile raf;
public static void main(String[] args) throws Exception {
//建立文件和内存的映射,即时双向同步
raf = new RandomAccessFile("D:/tmp/data.dat", "rw");
FileChannel fc = raf.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024);
//清除文件内容 ,对 MappedByteBuffer 的操作就是对文件的操作
for(int i=0;i<1024;i++){
mbb.put(i,(byte)0);
}
//从文件的第二个字节开始,依次写入 A-Z 字母,第一个字节指明当前操作的位置
for(int i=65;i<91;i++){
int index = i-63;
int flag = mbb.get(0); //可读标置第一个字节为 0
if(flag != 0){ //不是可写标示 0,则重复循环,等待
i--;
continue;
}
mbb.put(0,(byte)1); //正在写数据,标志第一个字节为 1
mbb.put(1,(byte)(index)); //文件第二个字节说明,写数据的位置
System.out.println(System.currentTimeMillis() + ":position:" + index +"write:" + (char)i);
mbb.put(index,(byte)i); //index 位置写入数据
mbb.put(0,(byte)2); //置可读数据标志第一个字节为 2
Thread.sleep(3000);
}
}
}
🌟🌟 2.4、读进程(不加锁)
public class NIORead {
private static RandomAccessFile raf;
public static void main(String[] args) throws Exception {
raf = new RandomAccessFile("D:/tmp/data.dat", "rw");
FileChannel fc = raf.getChannel();
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, 1024);
int lastIndex = 0;
for(int i=1;i<27;i++){
int flag = mbb.get(0); //取读写数据的标志
int index = mbb.get(1); //读取数据的位置,2为可读
if(flag != 2 || index == lastIndex){ //假如不可读,或未写入新数据时重复循环
i--;
continue;
}
lastIndex = index;
System.out.println( System.currentTimeMillis() + ":position:" + index +"read:" + (char)mbb.get(index));
mbb.put(0,(byte)0); //置第一个字节为可读标志为 0
if(index == 27){ //读完数据后退出
break;
}
}
}
}