5

Recently I learned about the existence StampedLock ?

https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/StampedLock.html I realized that It is improved ReentrantReadWriteLock with some differernces:

  • Is not reentrant
  • Supports optimistic lock
  • Supports upgrade from readLock to writeLock

Also I read examples frpm javadoc but I don't understant that code:

class Point {
  private double x, y;
  private final StampedLock sl = new StampedLock();
  // a read-only method
  // upgrade from optimistic read to read lock
  double distanceFromOrigin() {
    long stamp = sl.tryOptimisticRead();
    try {
      retryHoldingLock: for (;; stamp = sl.readLock()) {
        if (stamp == 0L)
          continue retryHoldingLock;
        // possibly racy reads
        double currentX = x;
        double currentY = y;
        if (!sl.validate(stamp))
          continue retryHoldingLock;
        return Math.hypot(currentX, currentY);
      }
    } finally {
      if (StampedLock.isReadLockStamp(stamp))
        sl.unlockRead(stamp);
    }
  }
}

What does it mean possibly racy reads ? [ANSWERRED IN COMMENTS]

Is it a problem if another thread reads x or y ? [ANSWERRED IN COMMENTS]

why do we execute tryOptimisticRead first and readLock in the for loop in case of tryOptimisticRead failure? what the logic?

Why do we have if (StampedLock.isReadLockStamp(stamp)) inside finally block vbefore unlock ?

wax
  • 2,400
  • 3
  • 19
  • 28
gstackoverflow
  • 36,709
  • 117
  • 359
  • 710
  • The "possibly racy reads" is because, on the first loop, the thread has tried to optimistically obtain the read lock but it may have failed. If the lock is valid (`validate`), then the read values are safe and it proceeds to use them. Otherwise, the read values are not safe and the loop continues, now obtaining the read lock via the blocking `readLock()` method. _At least, that's how I've always understood those examples._ – Slaw Apr 19 '19 at 19:00
  • @Slaw, it makes sense but: Is it a problem if another thread reads x or y ? – gstackoverflow Apr 19 '19 at 19:07
  • To expand on [comment by @Slaw](https://stackoverflow.com/questions/55766479/how-to-to-use-stampedlock-optimistic-locking-i-cant-understand-code-sample-fr#comment98207428_55766479), the javadoc of [`StampedLock`](https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/StampedLock.html), in the description of "Optimistic Reading", says: *"Fields read while in optimistic mode may be wildly inconsistent"*, so on that first iteration, if the object is updated between calls to `tryOptimisticRead()` and `validate()`, the `currentX` and `currentY` values may be inconsistent. – Andreas Apr 19 '19 at 19:07
  • @gstackoverflow No, it's a problem if another thread updates `x` or `y`. – Andreas Apr 19 '19 at 19:08
  • @Andreas so *validate* returns false only if writeLock was acquired after optimistic lock acquire ? – gstackoverflow Apr 19 '19 at 19:12
  • @Slaw why do we execute *tryOptimisticRead* first and *readLock* in the for loop in case of *tryOptimisticRead* failure ? – gstackoverflow Apr 19 '19 at 19:15
  • 1
    If the optimistic lock failed, either by `tryOptimisticRead()` returning `0` or by `validate()` returning `false`, the loop is retried (`continue`) to obtain a full read lock and run the logic again. Notice how only the 3rd part of the `for` statement is used, so that `readLock()` is only called on second iteration. Also realize that the loop will execute once (optimistic lock successful), or twice (optimistic lock failed), but never three times. – Andreas Apr 19 '19 at 19:25
  • The Javadoc, in the description of "Optimistic Reading", says: "_This mode can be thought of as an extremely weak version of a read-lock, that can be broken by a writer at any time. The use of optimistic read mode for short read-only code segments often reduces contention and improves throughput_". You use `tryOptimisticRead()` because you're optimistic the values will not change while reading them. But if they do then you upgrade to a full read lock and go again. – Slaw Apr 19 '19 at 19:27
  • @Slaw, but it is can't be broken by readLock? – gstackoverflow Apr 19 '19 at 19:29
  • Why would it? The read lock is non-exclusive—meaning multiple threads can read at the same time. – Slaw Apr 19 '19 at 19:30
  • @Slaw I just wanted to confirm it. So 2 threads can do optimistic reads in parallel and 2 another threads could do read at the same time. – gstackoverflow Apr 19 '19 at 19:34
  • @Andreas, But what the logic of this code snippet? Why do we try optimistic locking first and then try usual readLock. What the beneft of this approach? – gstackoverflow Apr 19 '19 at 19:37
  • 1
    Yes, just like any read-write lock, the read-lock is non-exclusive and the write-lock is exclusive; if there is a writer, no readers can proceed. And the benefit is mentioned in the Javadoc I quoted: "_The use of optimistic read mode for short read-only code segments often reduces contention and improves throughput_". Please note that `StampedLock` is not a drop-in replacement of `ReadWriteLock`—only use `StampedLock` if you're data structure is applicable and you've measured a performance gain. – Slaw Apr 19 '19 at 19:40
  • @ Slaw "The use of optimistic read mode for short read-only code segments often reduces contention and improves throughput" When it might help? When there are too much read operations which prevents to acquire writeLock ? – gstackoverflow Apr 19 '19 at 19:47
  • @gstackoverflow Yes, that is a very good example. --- Also, an optimistic lock is not actually a *lock*, so you might also save run time. – Andreas Apr 19 '19 at 19:58
  • You may find this answer, and the links it provides, to be helpful: https://stackoverflow.com/a/26100951/6395627 – Slaw Apr 19 '19 at 20:05
  • @Andreas But according my experiments writeLock always has higher priority: 1. At the moment 0 ms Thread 1 acquires readLock for 1 sec 2. At the moment 200 ms Thread 2 attempts to acquire writeLock and await 3. At the moment 500 ms Thread 3 attempts to acquire readLock and awaits. According to my experiments Thread 2 always acquires writeLock after first thread and before Thread 3 readLock – gstackoverflow Apr 19 '19 at 20:28
  • @ Slaw, please read topic update – gstackoverflow Apr 19 '19 at 21:00
  • The documentation explicitly states that write locks are not preferred over read locks or vice versa. And the `if (...)` bit in the `finally` block is because the stamp might not represent a read lock (i.e. it may represent the optimistic read lock). – Slaw Apr 19 '19 at 22:02
  • @Slaw, so for optimistic lock isReadLockStamp returns false? – gstackoverflow Apr 20 '19 at 07:41

1 Answers1

10

why do we execute tryOptimisticRead first and readLock in the for loop in case of tryOptimisticRead failure? what the logic?

A best case scenario is for us to to be able to read x and y without having to acquire a lock. That doesn't mean we don't establish a happens-before relationship, it just means we don't need to invoke a possible blocking action.

tryOptimisticRead returns us a stamp value. This volatile read of internal state establishes that anything written prior to the volatile write of this stamped value will be visible after a subsequent read. That means, if the stamped value returned in tryOptimisticRead doesn't change while you read x and y, then another write did not occur and we have the most up to date values. If the stamped value does change, however, all bets are off and you need to protect yourself explained below.

It's possible, and, depending on your use case, likely, that x and y will change at some point throughout your execution of distanceFromOrigin. If x and y change, and maybe change often, you're going to want to eventually succeed.

The readLock is the program's way of saying "Ok, I give up, let's just read it in a blocking fashion". In theory, you can write your code to tryOptimisticRead a few times before eventually invoking readLock, but you're going to want to give yourself an out in case x and y are updated constantly.

Why do we have if (StampedLock.isReadLockStamp(stamp)) inside finally block vbefore unlock ?

If readLock is invoked, you will have to release it prior to exiting so that subsequent writeLock can acquire the lock. If you succeed in tryOptimisticRead you will not have to release a readLock as you never needed to acquire it in the first place.

John Vint
  • 39,695
  • 7
  • 78
  • 108