线程和文件写入

时间:2012-04-02 07:41:32

标签: java multithreading

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

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

线程同步会有问题吗?有办法解决这个问题吗?

6 个答案:

答案 0 :(得分:30)

  

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

  

有办法解决这个问题吗?

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

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

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

如果他们各自使用自己的FileWriter或其他什么,请找到他们共享的其他内容作为互斥锁。

但是,再一次,代表其他人进行I / O操作可能也是一个好方法。

答案 1 :(得分:10)

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

答案 2 :(得分:2)

如果您想要任何性能和易管理性,请按照Alex和其他人的建议,使用生产者 - 消费者队列和一个文件编写器。让所有带有互斥锁的文件中的线程都很麻烦 - 每个磁盘延迟都会直接传输到您的主应用程序功能中(添加了争用)。对于速度缓慢的网络驱动器而言,这一点尤其不可靠

答案 3 :(得分:2)

嗯,没有任何实现细节,很难知道,但正如我的测试案例所示,我总是得到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();
            }
        }
    }
}

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

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

嗯,就像一群人在浴室外等候,没有排队......

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

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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