Reputation:
class Counter
{
public int i=0;
public void increment()
{
i++;
System.out.println("i is "+i);
System.out.println("i/=2 executing");
i=i+22;
System.out.println("i is (after i+22) "+i);
System.out.println("i+=1 executing");
i++;
System.out.println("i is (after i++) "+i);
}
public void decrement()
{
i--;
System.out.println("i is "+i);
System.out.println("i*=2 executing");
i=i*2;
System.out.println("i is after i*2"+i);
System.out.println("i-=1 executing");
i=i-1;
System.out.println("i is after i-1 "+i);
}
public int value()
{
return i;
} }
class ThreadA
{
public ThreadA(final Counter c)
{
new Thread(new Runnable(){
public void run()
{
System.out.println("Thread A trying to increment");
c.increment();
System.out.println("Increment completed "+c.i);
}
}).start();
}
}
class ThreadB
{
public ThreadB(final Counter c)
{
new Thread(new Runnable(){
public void run()
{
System.out.println("Thread B trying to decrement");
c.decrement();
System.out.println("Decrement completed "+c.i);
}
}).start();
}
}
class ThreadInterference
{
public static void main(String args[]) throws Exception
{
Counter c=new Counter();
new ThreadA(c);
new ThreadB(c);
}
}
In the above code, ThreadA first got access to Counter object and incremented the value along with performing some extra operations. For the very first time ThreadA does not have a cached value of i. But after the execution of i++ (in first line) it will get cache the value. Later on the value is updated and gets 24. According to the program, as the variable i is not volatile so the changes will be done in the local cache of ThreadA,
Now when ThreadB accesses the decrement() method the value of i is as updated by ThreadA i.e. 24. How could that be possible?
Upvotes: 1
Views: 218
Reputation: 563
How could that be possible?
Because there is nowhere in the JLS that says values have to be cached within a thread.
This is what the spec does say:
If you have a non-volatile variable x
, and it's updated by a thread T1
, there is no guarantee that T2
can ever observe the change of x
by T1
. The only way to guarantee that T2
sees a change of T1
is with a happens-before
relationship.
It just so happens that some implementations of Java cache non-volatile variables within a thread in certain cases. In other words, you can't rely on a non-volatile variable being cached.
Upvotes: 0
Reputation:
Now i came to know that common objects (the objects that are being shared by multiple threads) are not cached by those threads. As the object is common, Java Memory Model is smart enough to identify that common objects when cached by threads could produce surprising results.
Upvotes: 0
Reputation: 13066
As specified in JLS 8.3.1.4
:
The Java programming language allows threads to access shared variables (§17.1). As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables........A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable
Although not always but there is still a chance that the shared values among threads are not consistenly and reliably updated, which would lead to some unpredictable outcome of program. In code given below
class Test {
static int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
If, one thread repeatedly calls the method one (but no more than Integer.MAX_VALUE times in all), and another thread repeatedly calls the method two then method two could occasionally print a value for j that is greater than the value of i, because the example includes no synchronization and, the shared values of i and j might be updated out of order.
But if you declare i
and j
to be volatile
, This allows method one and method two to be executed concurrently, but guarantees that accesses to the shared values for i
and j
occur exactly as many times, and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, the shared value for j
is never greater than that for i
,because each update to i must be reflected in the shared value for i before the update to j occurs.
Upvotes: 0
Reputation: 1499860
Assuming that threads won't see each updates that other threads make to shared data is as inappropriate as assuming that all threads will see each other's updates immediately.
The important thing is to take account of the possibility of not seeing updates - not to rely on it.
There's another issue besides not seeing the update from other threads, mind you - all of your operations act in a "read, modify, write" sense... if another thread modifies the value after you've read it, you'll basically ignore it.
So for example, suppose i
is 5 when we reach this line:
i = i * 2;
... but half way through it, another thread modifies it to be 4.
That line can be thought of as:
int tmp = i;
tmp = tmp * 2;
i = tmp;
If the second thread changes i
to 4 after the first line in the "expanded" version, then even if i
is volatile the write of 4 will still be effectively lost - because by that point, tmp
is 5, it will be doubled to 10, and then 10 will be written out.
Upvotes: 5