Reputation: 427
I have a java program which uses 20 threads. Every one of them write their results in a file called output.txt
.
I always get a different number of lines in output.txt
.
Can it be a problem with the synchronization of threads? Is there a way to handle this?
Upvotes: 17
Views: 37578
Reputation: 4693
I'd suggest you to organize it this way: One thread-consumer will consume all data and write it to the file. All worker threads will produce data to the consumer thread in synchronous way. Or with multiple threads file writing you can use some mutex or locks implementations.
Upvotes: 12
Reputation: 12728
Well, without any implementation detail, it is hard to know, but as my test case shows, I always get 220 lines of output, i.e., constant number of lines, with FileWriter
. Notice that no synchronized
is used here.
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();
}
}
}
}
As I understand (and test), there are two phases in this process:
Well, it is just like a crowd waiting outside a bathroom, without queuing.....
So, if your implementation is different, show the code and we can help to break it down.
Upvotes: 1
Reputation: 65793
If you can hold your file as a FileOutputStream
you can lock it like this:
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);
}
}
Upvotes: 2
Reputation: 24847
If you want any semblance of performance and ease of management, go with the producer-consumer queue and just one file-writer, as suggested by Alex and others. Letting all the threads at the file with a mutex is just messy - every disk delay is transferred directly into your main app functionality, (with added contention). This is especially unfunny with slow network drives that tend to go away without warning.
Upvotes: 3
Reputation: 2762
You should use synchronization in this case. Imagine that 2 threads (t1 and t2) open the file at the same time and start writing to it. The changes performed by the first thread are overwrited by the second thread because the second thread is the last to save the changes to the file. When a thread t1 is writing to the file, t2 must wait until t1 finishes it's task before it can open it.
Upvotes: 1
Reputation: 1073978
can it be a problem of synchronization of threads?
Yes.
There's a way to handle this?
Yes, ensure that writes are serialized by synchronizing on a relevant mutex. Or alternately, have only one thread that actually outputs to the file, and have all of the other threads simply queue text to be written to a queue that the one writing thread draws from. (That way the 20 main threads don't block on I/O.)
Re the mutex: For instance, if they're all using the same FileWriter
instance (or whatever), which I'll refer to as fw
, then they could use it as a mutex:
synchronized (fw) {
fw.write(...);
}
If they're each using their own FileWriter
or whatever, find something else they all share to be the mutex.
But again, having a thread doing the I/O on behalf of the others is probably also a good way to go.
Upvotes: 33