线程和文件写入

我有一个使用20个线程的java程序。 他们每个人都将结果写在一个名为output.txt的文件中。

我总是在output.txt获得不同数量的行。

这可能是线程同步的问题吗? 有办法处理这个吗?

这可能是线程同步的问题吗?

是。

有办法解决这个问题吗?

是的,确保通过在相关互斥锁上同步来序列化写入。 或者,只有一个实际输出到该文件的线程,并且所有其他线程只是将队列文本排队写入一个写入线程从中抽取的队列。 (这样,20个主线程不会阻塞I / O.)

重新使用互斥锁:例如, 如果它们都使用相同的FileWriter实例(或其他),我将其称为fw ,那么它们可以将它用作互斥锁:

 synchronized (fw) { fw.write(...); } 

如果他们每个人都使用他们自己的FileWriter或其他什么,找到他们共享的其他东西作为互斥锁。

但同样,让一个代表其他人的I / O线程可能也是一个很好的方法。

我建议你这样组织它:一个线程消费者将消耗所有数据并将其写入文件。 所有工作线程都将以同步方式向使用者线程生成数据。 或者使用multithreading文件编写,您可以使用一些互斥锁或锁实现。

如果您想要任何性能和易管理性,请按照Alex和其他人的建议,使用生产者 – 消费者队列和一个文件编写器。 使用互斥锁将文件中的所有线程都放在一边 – 每个磁盘延迟都会直接传输到您的主应用程序function中(添加了争用)。 对于缓慢的网络驱动器而言,这种情况尤其不可靠,这些驱动器在没有警告的情

在这种情况下,您应该使用同步。 想象一下,2个线程(t1和t2)同时打开文件并开始写入它。 第一个线程执行的更改被第二个线程覆盖,因为第二个线程是最后一个将更改保存到文件的线程。 当线程t1写入文件时,t2必须等到t1完成它的任务才能打开它。

如果您可以将文件保存为FileOutputStream ,则可以将其锁定为:

 FileOutputStream file = ... .... // Thread safe version. void write(byte[] bytes) { try { boolean written = false; do { try { // Lock it! FileLock lock = file.getChannel().lock(); try { // Write the bytes. file.write(bytes); written = true; } finally { // Release the lock. lock.release(); } } catch ( OverlappingFileLockException ofle ) { try { // Wait a bit Thread.sleep(0); } catch (InterruptedException ex) { throw new InterruptedIOException ("Interrupted waiting for a file lock."); } } } while (!written); } catch (IOException ex) { log.warn("Failed to lock " + fileName, ex); } } 

好吧,没有任何实现细节,很难知道,但正如我的测试用例所示,我总是得到220行输出,即使用FileWriter常量行数。 请注意,此处未使用synchronized

 import java.io.File; import java.io.FileWriter; import java.io.IOException; /** * Working example of synchonous, competitive writing to the same file. * @author WesternGun * */ public class ThreadCompete implements Runnable { private FileWriter writer; private int status; private int counter; private boolean stop; private String name; public ThreadCompete(String name) { this.name = name; status = 0; stop = false; // just open the file without appending, to clear content try { writer = new FileWriter(new File("test.txt"), true); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { for (int i=0; i<20; i++) { new Thread(new ThreadCompete("Thread" + i)).start(); } } private int generateRandom(int range) { return (int) (Math.random() * range); } @Override public void run() { while (!stop) { try { writer = new FileWriter(new File("test.txt"), true); if (status == 0) { writer.write(this.name + ": Begin: " + counter); writer.write(System.lineSeparator()); status ++; } else if (status == 1) { writer.write(this.name + ": Now we have " + counter + " books!"); writer.write(System.lineSeparator()); counter++; if (counter > 8) { status = 2; } } else if (status == 2) { writer.write(this.name + ": End. " + counter); writer.write(System.lineSeparator()); stop = true; } writer.flush(); writer.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 

据我了解(和测试),这个过程分为两个阶段:

  • 池中的所有线程都已创建并启动,准备抓取文件;
  • 其中一个抓住它, 我猜它然后在内部锁定它 ,阻止其他线程获取访问权限,因为我从来没有看到一行来自两个线程的内容。 因此,当一个线程正在写入时,其他人正在等待它完成该行,并且很可能会释放该文件。 因此,不会发生竞争条件。
  • 其他人中最快的人抓住文件并开始写作。

好吧,就像一群人在浴室外等候,没有排队……

因此,如果您的实现不同,请显示代码,我们可以帮助将其分解。