[英]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.