javamultithreadingconcurrencylockingreentrantreadwritelock

ReentrantReadWriteLock. read and write acquire priority


I research ReentrantReadWriteLock

snippet from java doc:

The thread will not acquire the read lock until after the oldest currently waiting writer thread has acquired and released the write lock

Thus As I understood.

read duration- 1 time unit

write duration- 3 time unit

  1. time 0 - write lock acquired
  2. time 1 - read lock try read
  3. time 2 - write lock try write

Thus I expect following sequence:

  1. first write
  2. second write
  3. read

my experiment code:

public class RWLockCalculator {
    static long initTime = System.currentTimeMillis();
    private static int calculatedValue = 0;
    private static ReadWriteLock lock = new ReentrantReadWriteLock();
    public void calculate(int value) {
        lock.writeLock().lock();
        try {           
            System.out.println("write lock acquired at "+ (System.currentTimeMillis()-RWLockCalculator.initTime));
            this.calculatedValue = 1;
            Thread.sleep(300);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }

    public int getCalculatedValue() {
        lock.readLock().lock();
        try {           
            System.out.println("read lock acquired at "+ (System.currentTimeMillis()-RWLockCalculator.initTime));
            Thread.sleep(100);
            return calculatedValue;
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return -1;
        } finally {
            lock.readLock().unlock();
        }
    }
}

class Test {
    public static void main(String[] args) throws InterruptedException {
        new WriteThread().start();
        Thread.sleep(100);
        new ReadThread().start();
        Thread.sleep(100);
        new WriteThread().start();

    }
}

class ReadThread extends Thread {
    @Override
    public void run() {
        System.out.println(new RWLockCalculator().getCalculatedValue() + ", " + (System.currentTimeMillis() - RWLockCalculator.initTime));
    }
}

class WriteThread extends Thread {
    @Override
    public void run() {
        new RWLockCalculator().calculate(99);
        System.out.println("I have written in  " + (System.currentTimeMillis() - RWLockCalculator.initTime));
    }
}

out:

write lock acquired at 0
I have written in  300
read lock acquired at 300
1, 400
write lock acquired at 400
I have written in  700

Thus I get

  1. first write
  2. read
  3. second write

Why do I get this result?

Is it possible to break FIFO ordering?

Update

Please compare two sibling snippets from java doc(about fair mode):

first

A thread that tries to acquire a fair read lock (non-reentrantly) will block if either the write lock is held, or there is a waiting writer thread. The thread will not acquire the read lock until after the oldest currently waiting writer thread has acquired and released the write lock. Of course, if a waiting writer abandons its wait, leaving one or more reader threads as the longest waiters in the queue with the write lock free, then those readers will be assigned the read lock.

second:

A thread that tries to acquire a fair write lock (non-reentrantly) will block unless both the read lock and write lock are free (which implies there are no waiting threads). (Note that the non-blocking ReentrantReadWriteLock.ReadLock.tryLock() and ReentrantReadWriteLock.WriteLock.tryLock() methods do not honor this fair setting and will acquire the lock if it is possible, regardless of waiting threads.)

I do not fully understand the meaning of what is written there But I see that ReentrantReadWriteLock uses different politics for acquire read lock and write lock. I suggest that if politics were same in java doc wouldn't write two indents.

ReadLock can share locks. Is it only one difference?


Solution

  • First of all, ReentrantReadWriteLock should be created in fair mode to impose particular order of lock acquisition:

    private static ReadWriteLock lock = new ReentrantReadWriteLock(true);
    

    Then, javadoc describes you case pretty clearly:

    When constructed as fair, threads contend for entry using an approximately arrival-order policy. When the currently held lock is released either the longest-waiting single writer thread will be assigned the write lock, or if there is a group of reader threads waiting longer than all waiting writer threads, that group will be assigned the read lock.

    Since your reader thread waited longer than the second writer thread, it acquires a lock before the writer thread.