Reputation:
Assuming the following class:
public class Counter {
private long val;
private final ReadWriteLock reentrantLock = new ReentrantReadWriteLock();
public Counter(long val) {
this.val = val;
}
public void increment() {
try {
reentrantLock.writeLock().lock();
val++;
} finally {
reentrantLock.writeLock().unlock();
}
}
public long getVal() {
try {
reentrantLock.readLock().lock();
return this.val;
} finally {
reentrantLock.readLock().unlock();
}
}
}
Ignoring that we could use AtomicLong
, what bad things could happen when we read without a lock and why are these things happening.
My assumptions:
Another assumption:
Anything I'm missing?
Upvotes: 0
Views: 979
Reputation: 718916
The bad thing that can (in theory) happen is if readers don't use the read locks is that they could see a stale value of the counter; i.e. a value that is NOT the latest value written by a writer.
In Java, primitive locks and Lock
classes have two functions:
The provide mutual exclusion.
They provide threads with certain guarantees about visibility of values in shared variables.
Without the visibility guarantees provided by correct use of locks (and some other things), changes made by one thread may not be visible to another thread.
Unfortunately, while there is no guarantee that that one thread won't see the correct value, there is no guarantee that it will see an incorrect one either. The actual behavior depends on a number of different factors that are difficult to analyze ... and are implementation and platform dependent. So demonstrating that a thread can see stale values can be difficult. Likewise, you cannot prove that a program doesn't have that kind of flaw by testing it. And if a program does have this kind of flaw, it is likely to be hard to reproduce ... especially when you are using a debugger.
Not the latest value (a new writer thread could update the value +1 in the moment we read), so we would be at least one value behind.
In fact, the reader could see a value that many updates behind ... or even the initial value of val
.
Some garbage in-between writes value (can that happen in java)
This is also possible. The JMM treats a long
or a double
as two separate memory cells, so an reader that doesn't use locks could see a high word from one value and a low word from a different value.
There's no risk of not seeing any writer value, since the WriteLock enforces memory barriers which will flush the value to main memory.
This is incorrect from two respects:
That is an implementation detail. The JMM says nothing about memory barriers, and neither do the relevant javadocs.
In fact if the reader is not using a read lock, the JIT compiler might emit code that caches the value of val
in a register ... and not bother to re-read it from main memory in all circumstances.
Note that this is also an implementation detail. But this is a behavior that is permitted by the JMM. When the reader doesn't use the lock, there will be no happens before relation between the write and the subsequent read. With out that relation, the code doesn't need to meet the visibility guarantee.
Upvotes: 0