0

I need writers threads to have priority to access critical region over readers threads, can I use the ReadWriteLock interface to do this?

Juan Gil
  • 37
  • 7
  • When you say ‘have priority’, what do you want to happen to anything currently executing under a ‘read lock’ when a write lock arrives? Or so you just want a ‘writer’ to jump the queue, so that no more ‘reader’ ones start until the writer completes? – BeUndead Apr 24 '21 at 20:57
  • I want a writer to skip the queue – Juan Gil Apr 24 '21 at 21:00
  • [There is only one standard implementation of ReadWriteLock](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/concurrent/locks/ReentrantReadWriteLock.html) in Java SE, and its documentation says: “This class does not impose a reader or writer preference ordering for lock access.” There may exist other implementations of ReadWriteLock in third-party libraries, but Java SE doesn’t seem to have any others. – VGR Apr 24 '21 at 21:00
  • @VGR: there’s two (StampedLock has use as one too). Neither has priority though. You could probably write a solution with a ReadWriteLock if you wanted, but there’s probably simpler ways (using the Queue you’ve already alluded to, for example). – BeUndead Apr 24 '21 at 21:18
  • Java's non-fair locks are all _barging locks_, which mean they attempt to acquire immediately before falling back to being queued. This is not priority, but an optimization to avoid context switching if fairness is not required. A fair lock is slower, but is preferable when you need to safeguard against starving some threads. – Ben Manes Apr 25 '21 at 20:24

1 Answers1

2

While not directly with a ReadWriteLock, the simplest, in-built approach for something like this would probably be a Semaphore, which does support fairness. Creating a fair Semaphore, with an (effectively) unlimited number of permis should suffice:

private static final Semaphore lock = new Semaphore(Integer.MAX_VALUE, true);

public void doReadLocked() throws InterruptedException {

    // 'Read' lock only acquires one permit, but since there are A LOT,
    // many of them can run at once.
    lock.acquire();
    try {
        // Do your stuff in here...
    } finally {

        // Make sure you release afterwards.
        lock.release();
    }
}

public void doWriteLocked() throws InterruptedException {

    // 'Write' lock demands ALL the permits.  Since fairness is set, this
    // will 'take priority' over other waiting 'read'ers waiting to acquire
    // permits.
    lock.acquire(Integer.MAX_VALUE);
    try {
        // Do your stuff in here...
    } finally {

        // Make sure you release afterwards.
        lock.release(Integer.MAX_VALUE);
    }
}
BeUndead
  • 3,463
  • 2
  • 17
  • 21
  • According to [documentation](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Semaphore.html), the only kind of "fairness" is supported by Semaphores is **FIFO** one: when several threads wait for a semaphore, a thread which calls `.acquire()` **earlier** will win. So your comment for `lock.acquire(Integer.MAX_VALUE);` doesn't look as the correct one. ("this will 'take priority' over other waiting 'read'ers waiting to acquire permits.") – Tsyvarev Apr 26 '21 at 08:17
  • Any 'read' threads would be able to acquire the lock immediately. So with the 'write' lock being the first in the queue now, no more read locks will be able to be acquired until that write one has gone through. This is the behaviour the OP requested. There might be more to do to deal with if a second write arrives while the first is held though. – BeUndead Apr 26 '21 at 10:47
  • Oh, get it. But.. have you tested your code? I would expect that ``lock.acquire();`` won't wait for the `lock.acquire(Integer.MAX_VALUE);` but proceed immediately: the *fairness* is only about waiting. – Tsyvarev Apr 26 '21 at 11:36
  • 1
    @Tsyvarev https://ideone.com/LvWsBN I attempted a quick check here. Looks like it works as intended, but I might've messed up the test case. :/ The number in brackets at the start of each println is what I expect the count to be at that point. – BeUndead Apr 26 '21 at 12:54
  • Yes, it seems that after the writer calls `lock.acquire(Integer.MAX_VALUE)`, the reader with "Submitting READ" waits for existed reader and for the writer. Only when the writer releases the lock, the reader actually acquires it. – Tsyvarev Apr 26 '21 at 13:08
  • 2
    That’s not better than a `ReentrantReadWriteLock` in fair mode. When there is a pending `acquire(Integer.MAX_VALUE)`, the next `acquire()` will be enqueued after it, just like a pending read lock attempt will be enqueued when there is an older write lock attempt (in fair mode). But when another `acquire(Integer.MAX_VALUE)` attempt arrives, it will not overtake the earlier enqueued `acquire()` attempt, so there’s still no writer priority. – Holger Apr 26 '21 at 13:40