找回密码
 立即注册
首页 业界区 安全 Java 面试必问:文件读取如何保证读完?未写完就被读怎 ...

Java 面试必问:文件读取如何保证读完?未写完就被读怎么解决?

瞧蛀 3 天前
问题背景


  • 在 Java 文件操作的面试中,经常会问到两个核心问题:
  • 代码层面,如何判断一个文件已经完全读完?
业务层面,如何避免文件还在写入就被读取,读到不完整内容?

本文从基础判断到生产可用方案,一次性总结清楚
一、Java 中如何判断文件已读完?

不同 IO 方式,判断结束的标志不同,记住这一套即可:
  1. 1. 字节流 InputStream
  2. read() 或 read(byte[]) 返回 -1 表示到达文件末尾。
复制代码
  1. int len;
  2. byte[] buffer = new byte[1024];
  3. while ((len = inputStream.read(buffer)) != -1) {
  4.     // 处理数据
  5. }
  6. ---------------------------
  7. 1.字节流的 read() 方法返回 -1 时,表示文件读取到末尾(读完)。
  8. import java.io.FileInputStream;
  9. import java.io.IOException;
  10. public class ReadFileEndCheck {
  11.     public static void main(String[] args) {
  12.         try (FileInputStream fis = new FileInputStream("test.txt")) {
  13.             int byteData;
  14.             // 核心:read()返回-1时,说明文件读完
  15.             while ((byteData = fis.read()) != -1) {
  16.                 // 处理读取到的字节(比如转字符)
  17.                 System.out.print((char) byteData);
  18.             }
  19.             System.out.println("\n文件已完全读取");
  20.         } catch (IOException e) {
  21.             e.printStackTrace();
  22.         }
  23.     }
  24. }
  25. ---------------------------
  26. 2.批量读取(更高效):read(byte[] buffer) 返回读取到的字节数,返回 -1 时表示读完:
  27. try (FileInputStream fis = new FileInputStream("test.txt")) {
  28.     byte[] buffer = new byte[1024];
  29.     int len;
  30.     // len为-1时,文件读完
  31.     while ((len = fis.read(buffer)) != -1) {
  32.         // 处理buffer中前len个字节
  33.         System.out.print(new String(buffer, 0, len));
  34.     }
  35. } catch (IOException e) {
  36.     e.printStackTrace();
  37. }
复制代码
  1. 2. 字符流 BufferedReader
  2. readLine() 返回 null 表示读完。
复制代码
  1. String line;
  2. while ((line = br.readLine()) != null) {
  3.     // 处理行数据
  4. }
  5. --------------------
  6. import java.io.BufferedReader;
  7. import java.io.FileReader;
  8. import java.io.IOException;
  9. public class BufferedReaderEndCheck {
  10.     public static void main(String[] args) {
  11.         try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
  12.             String line;
  13.             // 核心:readLine()返回null时,文件读完
  14.             while ((line = br.readLine()) != null) {
  15.                 System.out.println(line);
  16.             }
  17.             System.out.println("文件已完全读取");
  18.         } catch (IOException e) {
  19.             e.printStackTrace();
  20.         }
  21.     }
  22. }
复制代码
  1. 3. NIO FileChannel
  2. channel.read(ByteBuffer) 返回 -1 表示读完。
复制代码
  1. import java.io.RandomAccessFile;
  2. import java.nio.ByteBuffer;
  3. import java.nio.channels.FileChannel;
  4. public class NIOFileRead {
  5.     public static void main(String[] args) {
  6.         try (RandomAccessFile raf = new RandomAccessFile("test.txt", "r");
  7.              FileChannel channel = raf.getChannel()) {
  8.             ByteBuffer buffer = ByteBuffer.allocate(1024);
  9.             int bytesRead;
  10.             // 返回-1时读完
  11.             while ((bytesRead = channel.read(buffer)) != -1) {
  12.                 buffer.flip(); // 切换为读模式
  13.                 while (buffer.hasRemaining()) {
  14.                     System.out.print((char) buffer.get());
  15.                 }
  16.                 buffer.clear(); // 清空缓冲区
  17.             }
  18.             System.out.println("\n文件已完全读取");
  19.         } catch (Exception e) {
  20.             e.printStackTrace();
  21.         }
  22.     }
  23. }
复制代码
二、核心问题:文件还没写完,就被读取了怎么办?
  1. 这类问题本质是:写读竞争 → 读到半拉文件。
  2. 常见场景:大文件上传、日志采集、跨进程文件传输。
  3. 下面是 4 种标准解决方案,按生产推荐度排序。
复制代码
三、方案 1:临时文件 + 重命名(最推荐、最稳定)
  1. 思路
  2. - 写入方先写 .tmp / .temp 临时文件
  3. - 完全写完后,原子重命名为正式文件名
  4. - 读取方只处理正式文件
复制代码
四、方案 2:约定结束标记(简单通用)
  1. 思路
  2. - 读写双方约定一个结束标记,例如:###EOF###
  3.   读取方只有读到该标记,才认为文件完整。
  4. 优点
  5.   实现简单
  6.   跨语言、跨平台
  7. 缺点
  8.   写入进程异常退出时,可能不会写结束符。
复制代码
五、方案 3:判断文件大小 / 修改时间稳定
  1. 思路
  2.   记录当前文件大小
  3.   等待 500ms ~ 1s
  4.   再次获取大小,不变则认为写入完成
复制代码
  1. long size1 = file.length();
  2. Thread.sleep(1000);
  3. long size2 = file.length();
  4. if (size1 == size2) {
  5.     // 认为写入完成
  6. }
复制代码
  1. 优点
  2.   无需改造写入端
  3.   适合第三方文件采集
  4. 缺点
  5.   写入暂停时可能误判
复制代码
六、方案 4:NIO 文件锁 FileLock
  1. 思路
  2.   写入时加独占锁
  3.   读取时尝试加共享锁,加锁成功表示可读取
复制代码
  1. FileLock lock = channel.tryLock(0, Long.MAX_VALUE, true);
  2. if (lock != null) {
  3.     // 文件未被占用,可以安全读取
  4.     lock.release();
  5. }
复制代码
  1. 优点
  2.   JVM 标准机制
  3.   适合多进程、多线程并发
  4. 缺点
  5.   不同操作系统行为略有差异
复制代码
七、面试标准回答(可直接背诵)
  1. 1. 判断文件读完:
  2.   字节流返回 -1,字符流 BufferedReader readLine 返回 null。
  3. 2. 防止未写完就读:
  4.   本质解决写读竞争问题,常用四种方案:
  5.     优先使用:临时文件 + 重命名,最稳定、生产最常用
  6.     简单方案:约定结束标记
  7.     无侵入方案:判断文件大小稳定
  8.     标准并发方案:NIO FileLock 文件锁
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册