views:

251

answers:

2

I'm building a system in Java which has to copy a subset of a file into another file (not necessarily to the beginning of the file). I.e. I have the following parameters:

File srcFile, File dstFile, long srcFileOffset, long dstFileOffset, long length

Several threads could be reading from the same source-file and writing to the same destination-file (although with different offsets) simultaneosuly so I need the process to be thread-safe (i.e. preventing other threads from seeking in the file while a thread is writing/reading).

How would you implement this? Should I use a RandomAccessFile or Java NIO? What kind of locks do I need to acquire? E.g. does a RandomAccessFile-instance automatically acquire a system-wide lock on the file or do I need to keep that separately?

Edit: Looks like randomAccessFile.getChannel().tryLock() acquires a system-wide lock but it's held by VM, not the calling thread. So if I'm using that then I need to acquire a lock for the thread as well (or two actually since I need to lock both the source- and destination-file).

+2  A: 

I've composed a small test code. Seems to work without problems on WinXP:

import java.io.RandomAccessFile;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MultiRAF {
    public static void main(String[] args) throws Exception {
        RandomAccessFile raf = new RandomAccessFile("testraf.dat", "rw");
        raf.setLength(4096);
        raf.close();
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int k = 0; k < 4; k++) {
            final int offset = k * 1024;
            final int kid = k;
            exec.submit(new Runnable() {
                public void run() {
                    try {
                        RandomAccessFile raf = new RandomAccessFile(
                            "testraf.dat", "rw");
                        for (int j = 0; j < 100; j++) {
                            System.out.printf("%d accessing%n", kid);
                            byte[] data = new byte[1024];
                            for (int i = 0; i < data.length; i++) {
                                data[i] = (byte)i;
                            }
                            raf.seek(offset);
                            raf.write(data);
                            System.out.printf("%d done%n", kid);
                        }
                        raf.close();
                    } catch (Exception ex) {
                        System.err.printf("%d failed%n", kid);
                        ex.printStackTrace();
                    }

                };
            });
        }
        exec.shutdown();
    }
}
kd304
Interesting, I guess the position set by seek is local for the RandomAccessFile-instance. I didn't know you could open several instances with write-access to the same file and still keep separate positions in the descriptor..
Yrlec
+1  A: 

you can just open a FileInputStream on srcFile, skip(srcFileOffset), and read from there. But to write on the destination file at a certain offset, you will need RandomAccessFile.

As for locking, you can use a singleton to read/write from the file and synchronize the read/write methods on your object.

Chochos
+1. Probably, the streamlining of random access should improve IO performance too.
kd304
Thanks, I'll try that. Then I just need to store the data read in some intermediate buffer so I don't write it byte-by-byte to the RandomAccessFile.
Yrlec