繁体   English   中英

线程和文件写入

[英]Threads and file writing

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

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

会不会是线程同步的问题? 有没有办法处理这个问题?

会不会是线程同步的问题?

是的。

有办法处理这个吗?

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

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

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

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

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

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

如果您想要任何外观的性能和易于管理,请按照 Alex 和其他人的建议,使用生产者-消费者队列和一个文件编写器。 让文件中的所有线程都使用互斥锁是很麻烦的 - 每个磁盘延迟都会直接传输到您的主应用程序功能中(增加了争用)。 这对于速度较慢的网络驱动器来说尤其可笑,这些驱动器往往会在没有警告的情况下消失。

如果您可以将文件作为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();
            }
        }
    }
}

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

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

嗯,就像一群人在卫生间外面等着,不用排队……

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM