Java 线程和文件写入

Java 线程和文件写入,java,multithreading,Java,Multithreading,我有一个java程序,它使用20个线程。他们每个人都把结果写在一个名为output.txt的文件中 我总是在output.txt中获得不同的行数 这可能是线程同步的问题吗?有办法解决这个问题吗 这可能是线程同步的问题吗 对 有办法解决这个问题吗 是,请确保通过在相关互斥体上同步来序列化写入。或者,只有一个实际输出到文件的线程,而所有其他线程只是将要写入的文本排队到一个写入线程从中提取的队列中。(这样20个主线程就不会阻塞I/O。) 关于互斥体:例如,如果他们都使用相同的FileWriter实例(

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

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

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

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

有办法解决这个问题吗

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

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

synchronized (fw) {
    fw.write(...);
}
如果他们每个人都在使用自己的
FileWriter
或其他任何东西,请找到他们共享的其他东西作为互斥体


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

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

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

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

如果您可以将文件作为
文件输出流保存,您可以像这样锁定它:

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);
  }
}

好的,没有任何实现细节,很难知道,但是正如我的测试用例所示,我总是使用
FileWriter
获得220行输出,即恒定的行数。请注意,此处不使用
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();
            }
        }
    }
}
导入java.io.File;
导入java.io.FileWriter;
导入java.io.IOException;
/**
*同步竞争写入同一文件的工作示例。
*@author WesternGun
*
*/
公共类ThreadCompete实现可运行{
私有文件编写器;
私人身份;
专用int计数器;
私有布尔停止;
私有字符串名称;
公共线程(字符串名称){
this.name=名称;
状态=0;
停止=错误;
//只需打开文件而不附加内容,即可清除内容
试一试{
writer=newfilewriter(新文件(“test.txt”),true;
}捕获(IOE异常){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
}
公共静态void main(字符串[]args){
对于(int i=0;i 8){
状态=2;
}
}否则如果(状态==2){
writer.write(this.name+“:End.+计数器);
writer.write(System.lineSeparator());
停止=真;
}
writer.flush();
writer.close();
}捕获(IOE异常){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
}
}
}
据我了解(和测试),此过程分为两个阶段:

  • 池中的所有线程都已创建并启动,准备抓取文件
  • 其中一个抓取了它,我猜它会在内部锁定它,阻止其他线程访问,因为我从来没有看到来自两个线程的内容组合。因此,当一个线程正在写入时,其他线程将等待它完成该行,并且很可能会释放该文件因此,不会发生任何比赛条件。
  • 其他人中最快的一个抓起文件开始写
嗯,这就像一群人在浴室外等待,没有排队


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

+1在您编写您的答案时,我只是在我的答案中添加了此建议。:-)请您具体说明一下,对于单编写器线程方法,什么是最好的方法?例如,可能使用一个文件,只是将任务提交给该
执行者
?这是完全多余的,因为存在
已同步的
。@EJP-请参阅-文件上的锁应该对所有有权访问该文件的程序可见,而不管这些程序是用什么语言编写的-因此,理论上,它们应该比
synchronized
更好,但通常不是。这并不意味着它是非冗余的,或者“比
synchronized
更好”。这个问题中没有关于其他进程或其他语言的内容。嗯,您的实现如何还不是很清楚。正如我的简单测试用例所示,我使用FileWriter和20个线程获得恒定的输出行。可能需要添加一些实现细节。查看我的答案。您可以尝试在两行writer.write()之间添加睡眠。通过这种方式,您可能会看到它不是线程安全的。这不是一个真正的多线程示例,因为for循环将串行调用线程。要真正模拟多线程,可以使用