Is a read lock on ReentrantReadWriteLock, sufficient to read RandomAccessFile at the same time

I am writing something to handle concurrent read / write requests to a database file.

ReentrantReadWriteLock looks good. If all threads access the shared RandomAccessFile object, do I need to worry about a file pointer with parallel readers? Consider this example:

import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.concurrent.locks.ReentrantReadWriteLock; public class Database { private static final int RECORD_SIZE = 50; private static Database instance = null; private ReentrantReadWriteLock lock; private RandomAccessFile database; private Database() { lock = new ReentrantReadWriteLock(); try { database = new RandomAccessFile("foo.db", "rwd"); } catch (FileNotFoundException e) { e.printStackTrace(); } }; public static synchronized Database getInstance() { if(instance == null) { instance = new Database(); } return instance; } public byte[] getRecord(int n) { byte[] data = new byte[RECORD_SIZE]; try { // Begin critical section lock.readLock().lock(); database.seek(RECORD_SIZE*n); database.readFully(data); lock.readLock().unlock(); // End critical section } catch (IOException e) { e.printStackTrace(); } return data; } } 

In the getRecord () method, is the following rotation possible with multiple parallel readers?

Theme 1 -> getRecord (0)
Theme 2 -> getRecord (1)
Theme 1 β†’ acquires a common castle
Theme 2 β†’ acquires a common castle
Theme 1 β†’ seeks to write 0
Theme 2 β†’ seeks to write 1
Topic 1 β†’ reading a record in a file pointer (1)
Thread 2 β†’ reads an entry in the file pointer (1)

If there are really potential concurrency problems using ReentrantReadWriteLock and RandomAccessFile, what would be the alternative?

+4
source share
5 answers

Yes, this code is not properly synchronized, as you outline. A read and write lock is not useful if a write lock is never obtained; it is like there is no blockage.

Use the traditional synchronized block to make searching and reading appear atomic for other threads or to create a pool of RandomAccessFile instances that are borrowed for the exclusive use of one thread and then returned. (Or just allocate a channel for each thread if you have too many threads.)

+2
source

This is an example program that locks a file and unlocks a file.

 try { // Get a file channel for the file File file = new File("filename"); FileChannel channel = new RandomAccessFile(file, "rw").getChannel(); // Use the file channel to create a lock on the file. // This method blocks until it can retrieve the lock. FileLock lock = channel.lock(); // Try acquiring the lock without blocking. This method returns // null or throws an exception if the file is already locked. try { lock = channel.tryLock(); } catch (OverlappingFileLockException e){} lock.release(); // Close the file channel.close(); } catch (Exception e) { } 
+4
source

You might want to consider using file system locks instead of managing your own locks.

Call getChannel().lock() on RandomAccessFile to lock the file using the FileChannel class. This prevents write access even from processes outside your control.

+2
source

Work with a single lock object rather than with a method, ReentrantReadWriteLock can support up to 65535 recursive write locks and 65535 read locks.

Assign read and write lock

 private final Lock r = rwl.readLock(); private final Lock w = rwl.writeLock(); 

Then work on them ...

In addition: you do not serve the exception and cannot be able to unblock it after blocking. Call the lock when you enter a method (for example, a mutex lock), then do your work in a try / catch block with unlock in the finally section, for example:

 public String[] allKeys() { r.lock(); try { return m.keySet().toArray(); } finally { r.unlock(); } } 
+1
source

OK, 8.5 years is a long time, but I hope it is not necro ...

My problem was that we needed to access the threads in order to read and write as atomic as possible. The important part was that our code had to work on several computers accessing the same file. However, all the examples on the Internet settled on explaining how to block RandomAccessFile , and did not go deeper. So my starting point was Sam's answer .

Now, from a distance it makes sense to have a certain order:

  • lock file
  • open streams
  • do with threads
  • close streams
  • release the lock

However, to release the lock in Java, threads should not be closed! Because of this, the whole mechanism becomes a bit strange (and wrong?).

To perform an automatic close, remember that the JVM closes objects in the reverse order of the try segment. This means the stream is as follows:

  • open streams
  • lock file
  • do with threads
  • release the lock
  • close streams

Tests have shown that this will not work. Therefore, automatically close halfway and do the rest in a good "Java 1" style:

 try (RandomAccessFile raf = new RandomAccessFile(filename, "rwd"); FileChannel channel = raf.getChannel()) { FileLock lock = channel.lock(); FileInputStream in = new FileInputStream(raf.getFD()); FileOutputStream out = new FileOutputStream(raf.getFD()); // do all reading ... // that moved the pointer in the channel to somewhere in the file, // therefore reposition it to the beginning: channel.position(0); // as the new content might be shorter it a requirement to do this, too: channel.truncate(0); // do all writing ... out.flush(); lock.release(); in.close(); out.close(); } 

Note that methods using this must be synchronized . Otherwise, parallel executions can raise an OverlappingFileLockException when calling lock() .

Share your experience if you have ...

0
source

All Articles