1. 引言

在Java中处理大文件时,性能问题常常让人头疼。使用独立线程进行文件操作可以避免阻塞主线程,显著提升效率。本文将深入探讨如何通过多线程实现高效的文件读写操作。

2. 为什么需要多线程

单线程程序处理文件时,读写操作必须串行执行——先读完整个文件,再写入目标文件。这种模式在处理大文件时效率低下,而多线程能充分利用多核CPU的优势,让I/O操作与计算任务重叠执行

✅ 多线程的核心优势:

  • 并发执行多个文件操作
  • 充分利用多核处理器资源
  • 减少整体执行时间

⚠️ 但需要注意:多线程效果取决于具体任务类型和I/O特性,并非所有场景都适用。

3. 多线程文件操作实现

3.1. 独立线程读取文件

创建新线程时,我们使用FileReader配合BufferedReader实现高效逐行读取:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});

thread.start();

3.2. 独立线程写入文件

写入操作同样通过独立线程实现,使用FileWriter类:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        try (FileWriter fileWriter = new FileWriter(filePath)) {
            fileWriter.write("Hello, world!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});

thread.start();

当读写操作相互独立时,这种并发模式能最大化发挥性能优势

4. 并发控制问题

多线程同时访问文件时,必须警惕数据竞争风险: ❌ 读写线程并发执行可能导致:

  • 读取到不完整或部分写入的数据
  • 多个写线程相互覆盖数据

解决方案:采用生产者-消费者模型,通过队列协调读写操作:

  • 生产者线程读取文件并加入队列
  • 消费者线程从队列取出数据并处理

5. 使用BlockingQueue实现并发处理

BlockingQueue是线程安全的队列实现,其内部锁机制确保每次只有一个线程能访问队列,完美解决并发问题。

5.1. 创建FileProducer

生产者类负责读取文件行并加入队列:

class FileProducer implements Runnable {
    private final BlockingQueue<String> queue;
    private final String inputFileName;

    public FileProducer(BlockingQueue<String> queue, String inputFileName) {
        this.queue = queue;
        this.inputFileName = inputFileName;
    }
    
    @Override
    public void run() {
        try (BufferedReader reader = new BufferedReader(new FileReader(inputFileName))) {
            String line;
            while ((line = reader.readLine()) != null) {
                queue.offer(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5.2. 创建FileConsumer

消费者类从队列取出数据并写入文件:

class FileConsumer implements Runnable {
    private final BlockingQueue<String> queue;
    private final String outputFileName;

    public FileConsumer(BlockingQueue queue, String outputFileName) {
        this.queue = queue;
        this.outputFileName = outputFileName;
    }
    
    @Override
    public void run() {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(outputFileName))) {
            String line;
            while ((line = queue.poll()) != null) {
                writer.write(line);
                writer.newLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5.3. 线程调度器

主程序中创建队列并启动线程:

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

String fileName = "input.txt";
String outputFileName = "output.txt";

Thread producerThread = new Thread(new FileProducer(queue, fileName));
Thread consumerThread = new Thread(new FileConsumer(queue, outputFileName));

producerThread.start();
consumerThread.start();

try {
    producerThread.join();
    consumerThread.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

测试输入文件:

Hello,
Baeldung!
Nice to meet you!

执行后输出文件内容一致:

Hello,
Baeldung!
Nice to meet you!

关键点:队列允许多行数据同时存在,消费者可在生产者仍在添加数据时就开始处理。

6. 总结

本文展示了Java中通过多线程优化文件操作的方法,重点介绍了使用BlockingQueue实现线程安全的生产者-消费者模式。这种方案能有效协调读写操作,避免数据竞争问题。

完整示例代码可在GitHub获取。


原始标题:Read and Write Files in Java Using Separate Threads | Baeldung