views:

293

answers:

4

Hi,

After some serious googleing I found out that the RandomAccessFile-class is not thread-safe. Now I could use one semaphore to lock all reads and writes but I don't think that performs very well. In theory it should be possible to do multiple reads and one write at a time. How can I do this in Java? Is it possible at all?

Thanks!

+6  A: 

I could use one semaphore to lock all reads and writes but I don't think that performs very well.

With respect to performance, NEVER think. ALWAYS measure.

That said, java.util.concurrent.locks.ReentrantReadWriteLock is what you are looking for.

Kilian Foth
Yes, but then I'll be doing 2 or more concurrent reads, RandomAccessFile won't handle that.See: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4193259 (the 'evaluation' part)
Folkert van Heusden
So then you can't use a read lock so you may as well use synchronization.
EJP
+1  A: 

Partial locking of a file is a complex business which a lot of operating systems avoid doing. However if you insist on doing it, one way is to design your own locking mechanism object that records which parts of the file are locked. Essentially before reading or writing an object must request a lock for a specific byte range of the file. Locks are considered to clash if they overlap at all in byte range. Read and write locks are treated differently: a read can overlap with any number of read locks safely, but a write lock must overlap with no other locks, read or write. There are a lot of questions about whether to wait or abort if you can't get the lock, and whether to block reads while a write is waiting, but only you can answer them about your application.

Given the complexity of this it may be better to lock the entire file. Check to see if you get adequate performance - and don't forget you can allow multiple reads at once, as long as there are no writes.

DJClayworth
hmmm interesting idea. I have a readblock(int nr) and a writeblock(int nr). now I could have an array Semaphore [] locks = new Semaphore[10]; then in readblock/writeblock() I could do locks[nr % 10].acquireUninterruptibly()can't think of any problems with that.altough I still think that the underlying RandomAccessFile will fail with concurrent access
Folkert van Heusden
The point of making a locking object is to prevent there ever being a concurrent access. You have to code so that all threads have to get a lock from the locking object before accessing the file, and release it after they are done.
DJClayworth
+1  A: 

Consider this approach - it allows unlimited readers, and when a writer wants to write, it waits for current readers to finish to do its write.

class readWriteSemaphore() {
    private Object lock;
    List<Thread> readers;
    Thread writer;

    readWriteSemaphore() {
        readers = new LinkedList<Thread>(); // Linked list is inefficient for many threads, FYI
        writer = null;
    }

    /**
    * Returns true if and only if you have acquired a read
    * maybe use while(!rws.acquireRead(Thread.currentThread())) Thread.sleep(50); // or something
    */
    boolean acquireRead(Thread t) {
        synchronized(lock) {
            if(writer == null) {
                readers.add(t);
                return true;
            }
            return false; // yes this could go outside the synch block... oh well
        }
    }

    void releaseRead(Thread t) {
        synchronized(lock) {
            while(readers.remove(t)); // remove this thread completely
        }
    }

    boolean acquireWrite(Thread t) {
        synchronized(lock) {
            if(writer == null) return false;
            writer = t;
        }
        while(readers.size() > 0) Thread.sleep(50); // give readers time to finish. 
        //They can't re-enter yet because we set the writer,
        // if you attempt to acquire a write, future reads will be false until you're done
        return true;
    }

    void releaseWrite(Thread t) {
        synchronized(lock) {
            if(t != writer) throw new IllegalArgumentException("Only writer can release itself");
            writer = null;
        }
    }

}
glowcoder
Looks like a java.util.concurrent.locks.ReentrantReadWriteLock implementation?But still: I need a way for multiple reads and/or writes to succeed. And RandomAccessFile doesn't allow multiple reads.
Folkert van Heusden
This allows for multiple reads. Just not multiple writes.
glowcoder
Yes, but RandomAccessFile doesn't allow multiple reads.I'm looking for some other class doing so.
Folkert van Heusden
+1  A: 

If a simple mutex on the entire file is going to give you a performance bottleneck, and RandomAccessFile is not thread-safe without a mutex, then you need to look at alternatives to RandomAccessFile.

One alternative is to map the file into memory as a MappedBuffer and use slices of the buffer to allow different threads to access the file without interfering with each other. Single writer / multiple reader locking at the granularity of the entire would be easy to implement. You could also go further and implement concurrent reading and writing of non-overlapping sections of the file, but that would be more complicated.

I would not be surprised to hear that someone, somewhere has already implemented this as a reusable library.

Stephen C