I got into a discussion with Jevgeni and Heinz about some code that Jevgeni was working on. He needed threads to do something every 1/2 second. These thread could apparently look at the timer very very frequently so he didn't want to synchronize. But without synchronization you don't have thread safety do we. Well maybe we do. The clearist way to achieve this is to declare the variable volatile. But doing so will cause the thread to reload the value by-passing cache and this is something Jevgeni wanted to avoid. Borrowing an idea from Cliff Click's non blocking concurrency work I suggested something like this
int counter;
volatile int writefence;
public void run() {
// do stuff
counter += 1;
writefence++;
}
public int getCounter() {
return this.counter;
}
So, the thread responsible for maintain counter will change that value very infrequently in Jevgeni's use case. When it does write, it will then write to a volatile int. That write should cause the cache to flush values out to memory. The next read will refresh the CPU cache with a current value. So whle some read threads may slip through, it's not important in Jevgeni's case because they treads visit often. What more important, the writer working slowly only causes the disruption in the CPU infrequently where as the readers never trigger a refresh. Now to think about how this is broken... if it is.
I know you know better than this. You have no garantuee that the new
counter value will ever arrive on the reading side. #getCounter might get
inlined and since the read to this.counter doesn't carry any
synchronization, it might get elided or moved out of a loop. As always, you
need to construct a logical causality (or happens-before) between the write
and the read.
Hmm. I don't see a CAS here. Maybe your example is not showing everything.
But anyway, you always need synchronization at both ends. There's no way
around it. Enough inlining and optimization could otherwise turn the reader
into "int counter = this.counter; while(mainloop) { // counter is constant
here }"
To put some meat to the discussion: is the following what you have in mind?
If so, it doesn't work. Doesn't print anything for me. When I make counter
volatile, it does.
int counter;
volatile int writefence;
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
counter += 1;
writefence++;
}
public int getCounter() {
return this.counter;
}
private static void doSomethingElse() {
}
public static void main(String[] args) {
final Hugh hugh = new Hugh();
new Thread() {
@Override
public void run() {
while(true) {
hugh.run();
}
}
}.start();
new Thread() {
@Override
public void run() {
int lastCounter = 0;
while(true) {
int nextCounter;
while((nextCounter = hugh.getCounter()) == lastCounter) {
doSomethingElse();
}
System.out.println("Counter updated to " + nextCounter);
lastCounter = nextCounter;
}
}
}.start();
}
What about statements reordering? I believe that compiler/hotspot is free
to change
counter++;
@cronin:
> What about statements reordering? I believe that compiler/hotspot is
free to change
> counter++; writefence++; into writefence++; counter++;
Matthias is right. While this trick might work on your particular CPU with
your specific JVM, the memory model does not guarentee it to keep working
if these variables change.
He's worried about the synchronization overhead of synchronizing every half
second? What's he programming, a Commodore 64?
although the read counter may be properly updates since the volatile will
issue a memory fence the reading thread is *not* guaranteed to see the
change unless there is any memory fence before the reading.
Checking volatile might be an issue but definitely not one for "high" pulse
of half a second.