Reputation: 115
I read it from Java Concurrency in Practice, that it is bad to share variables in threads without synchronisation. However, for some examples as following which only have one read thread and one write thread, I can't find errors in it. From my perspective, the result for the following program will definitely terminate and print 42 because ReaderThread can go through only when ready becomes true, and that means number is 42. Could somebody give me some explanation why I am wrong?
public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready)
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
Upvotes: 3
Views: 578
Reputation: 1074295
You don't need synchronization (e.g., synchronized
) in your example (though you do need volatile
, more below) because reads and writes of boolean
and int
variables are always atomic. Which is to say, a thread can't be part-way through writing to a boolean
(or int
) variable when another thread comes along and reads it, getting garbage. The value being written by one thread is always fully written before another thread can read it. (This is not true of non-volatile
double
or long
variables; it would be entirely possible for a thread to read garbage if it happened to read in the middle of another thread's write to a long
or double
if they aren't marked volatile
.)
But you do need volatile
, because each thread can have its own copy of the variables, and potentially can keep using its own copy for a long period of time. So it's entirely possible for your reader thread to wait forever, because it keeps re-reading its own copy of ready
which stays false
even though your main thread writes true
to its copy of ready
. It's also possible for your reader thread to see ready
become true
but keep reading its own copy of number
, and so print 0 instead of 42.
You would need to use synchronized
if you were modifying the state of an object that doesn't guarantee thread-safe access. For instance, if you were adding to a Map
or List
. That's because there are multiple operations involved, and it's essential to prevent one thread from reading a half-complete change another thread is making.
Other classes, such as those in java.util.concurrent
, offer classes with thread-safe access semantics.
Upvotes: 3
Reputation: 73558
Since ready
isn't volatile
, there's no guarantee that ReaderThread
will see that your main thread has changed it. When you mark ready
as volatile, all writes from one thread will be seen in other threads reading it.
You always need some sort of synchronization / visibility control when communicating between threads. Whether it's volatile
, explicitly using synchronized
or using the java.util.concurrent.*
classes.
Upvotes: 3