7

i have 20 threads that write with the println() function on a file called results.txt. How can i synchronize them all?

I note every time my program run i have different number of lines of text in results.txt.

Thank you.

Marco Micheli
  • 707
  • 3
  • 8
  • 26

3 Answers3

30

Access the file through a class that contains a synchronized method to write to the file. Only one thread at a time will be able to execute the method.

I think that Singleton pattern would fit for your problem:

package com.test.singleton;

public class Singleton {
    private static final Singleton inst= new Singleton();
    
    private Singleton() {
        super();
    }
    
    public synchronized void writeToFile(String str) {
        // Do whatever
    }
    
    public static Singleton getInstance() {
        return inst;
    }
    
}

Every time you need to write to your file, you only would have to call:

Singleton.getInstance().writeToFile("Hello!!");
Sergio
  • 27,326
  • 8
  • 128
  • 149
BWitched
  • 484
  • 4
  • 6
  • 2
    +1 Simple, but effective. However, "a *class*" should be "an *object*" (instantiated from a class with said synchronized methods) as `synchronized` methods are only mutually-exclusive per-object. –  Apr 05 '12 at 10:47
  • 1
    so i have to write a "I/O" class and call it in the code like a object to print? – Marco Micheli Apr 05 '12 at 10:48
  • 2
    @MarcoMicheli The key point in this answer is: [`synchronized`](http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html) –  Apr 05 '12 at 10:49
  • 2
    (Technically, [static methods can be synchronized too](http://stackoverflow.com/questions/578904/how-do-synchronized-static-methods-work-in-java), but down with static methods!) –  Apr 05 '12 at 10:53
  • 4
    I would use an `enum` for a singleton. – Peter Lawrey Apr 05 '12 at 12:15
  • 10
    getInstance() method needs to be static – vilpe89 Mar 02 '15 at 20:17
  • Where do you specify the filename in these methods? – Kishan Mehta Feb 01 '19 at 05:40
7

Duplicate question ... duplicate answer. As I said here:

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);
  }
}
Community
  • 1
  • 1
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • 2
    From FileLock's JavaDoc: "File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine." – theadam Jul 26 '13 at 08:19
  • 1
    @theadam - That quote is immediately followed by *File-lock objects are safe for use by multiple concurrent threads.* - just saying – OldCurmudgeon Jul 26 '13 at 08:35
  • 2
    Multiple threads can safely use the obtained FileLock object, however this lock is NOT suitable for controlling access to a file by multiple threads. That is - the object (FileLock) itself is threadsafe. It shouldn't be used as you describe it though. – theadam Jul 27 '13 at 09:08
2

You are intend to write data into one file. So if you try to lock the whole file, it'd better to use a single thread to do this job. Although you spawn 20 threads, but there is only one of them is running every time you call the method, the others are just waiting for the lock.

I recommend you use RandomAccessFile to write data to your file. Then each thread can write some unique data into to the file without locking the whole file.

Some demo code as following

try {
    final RandomAccessFile file = new RandomAccessFile("/path/to/your/result.txt", "rw");
    final int numberOfThread = 20;
    final int bufferSize = 512;
    ExecutorService pool = Executors.newFixedThreadPool(numberOfThread);
    final AtomicInteger byteCounter = new AtomicInteger(0);
    final byte[] yourText = "Your data".getBytes();
    for (int i = 0; i < yourText.length; i++) {
        pool.submit(new Runnable() {
            @Override
            public void run() {
                int start = byteCounter.getAndAdd(bufferSize);
                int chunkSize = bufferSize;
                if (start + bufferSize > yourText.length) {
                    chunkSize = yourText.length - start;
                }
                byte[] chunkData = new byte[chunkSize];
                System.arraycopy(yourText, start, chunkData, 0, chunkSize);
                try {
                    file.write(chunkData);
                } catch (IOException e) {
                    //exception handle
                }
            }
        });
    }
    file.close();
} catch (Exception e) {
    //clean up
}
George
  • 4,029
  • 2
  • 22
  • 26