In the below code total is always 45, but total1 is unpredictable. I feel total should be unpredictable too as threads are not depending whether lock is acquired by tryLock() or not. Please can anybody explain what is the difference in calculation of total and total1.
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
import java.util.stream.*;
public class HelloWorld{
private Lock vault = new ReentrantLock();
private int total = 0;
private int total1 = 0;
public void deposit(int value) {
try {
vault.tryLock();
total = value;
}
finally {
vault.unlock();
}
}
public void add(int val) {
total1 = val;
}
public static void main(String[] unused) {
HelloWorld bank = new HelloWorld();
IntStream.range(1, 10)
.parallel()
.forEach(s -> bank.deposit(s));
System.out.println(bank.total); // 45
HelloWorld bank1 = new HelloWorld();
AtomicLong value1 = new AtomicLong(0);
final long[] value2 = {0};
IntStream.iterate(1, i -> 1).limit(100)
.parallel()
.forEach(i -> value1.incrementAndGet());
IntStream.iterate(1, i -> 1).limit(100)
.parallel()
.forEach(i -> value2[0]);
IntStream.iterate(1, i -> 1).limit(100)
.parallel()
.forEach(i -> bank1.add(i));
System.out.println(value1 " " value2[0] " " bank1.total1); // 100 94(Unpredictable) 96 (Unpredictable)
}
}
CodePudding user response:
I feel total should be unpredictable too as threads are not depending whether lock is acquired by tryLock() or not.
I think you are right... Sort of.
Your program seems to allow different threads to access total
with no synchronization (i.e., when vault.tryLock()
happens to fail), but just because it is allowed to happen, that's not a guarantee that it actually will happen.
Testing for concurrency-related bugs sometimes works, and sometimes doesn't work. The Java language spec and the javadoc for the standard Java library guarantee certain behaviors if your program obeys certain rules, but they do not guarantee that your program will behave "wrongly" if it breaks the rules. They allow it, but they don't guarantee it.
CodePudding user response:
To make things confusing total
and total1
aren't even calculating the same thing:
total
is trying to compute1 2 3 4 5 6 7 8 9
total1
is trying to compute1 1 1 1 1 ...
If we just consider the threading though:
total
calls thedeposit
method which is guarded by the lock.total1
calls theadd
method which is not guarded.
Therefore total
is predictable because the lock means only one thread at a time can do the add operation, but total1
is unpredictable because many threads could be doing each add operation at the same time.