Although there are a lot of posts on SO and elsewhere about the happens-before relationship, I am having a surprisingly hard time finding a definitive answer to my question.
Consider the two Java threads:
initially, flag == false
and data == 0
T1
data = 42;
synchronized(m) {
flag = true;
}
T2
boolean f;
synchronized(m) {
f = flag;
}
if (f) {
int x = data;
}
According to the code above, I believe f
could either be assigned the value true
or false
, there is no guaruntee. Is this correct?
Now, if the two synchronized
statements were changed to synchronized(flag)
, I think the instruction flag = true
will always happen before the instruction f = flag
, and thus f
will always be assigned the value true
. Is this correct?
No, synchronized make no guarantees about which Thread
gets there first. In only guarantees that more that one Thread
cannot access the synchronized block at a time.
Think of a synchronized
block as a room with a lock on the door. We have no idea who will get to the door first, but once they do they go in and lock the door.
Other people must wait until the person in the room is ready to leave and unlocks the door. Once that happens everyone races to get in the door again.
It all depends on the order in which you set your Thread
s off. But even then there are no guarantees as the JVM may suspend Thread
s and Thread
s yield
to one-another randomly. In the analogy - the guy in the lead to get to the door might trip on a banana peel. No likely, but always possible.
If you want to have guarantees then you need to wait
/ notify
- so that the reading thread checks the flag
, and if it's false
, suspends itself in a loop.
The writing thread then sets the flag
and notifies the lock monitor which wakes the reading thread.
Here is an example using the Lock
and Condition
api from Java 5. I'm not saying that you have to use that in preference to synchronized
and wait
/ notify
- it's just an example I had lying around that I adapted.
The Reader
acquires the lock
and then checks the ready
flag in a loop. If the flag is false
it await
s on the condition. This atomically releases the lock
and suspends the Thread
.
The Writer
meanwhile acquires the lock
and sets the data
and the ready
flag. It then calls signalAll
and releases the lock
.
This wakes the Reader
which then reads the ready
as true
and proceeds to the print
statement.
The output will always be 42
(never -1
).
public class App {
static volatile boolean ready = false;
static volatile int data = -1;
private static class Reader implements Runnable {
private final Lock lock;
private final Condition condition;
public Reader(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
try {
while (!ready) {
try {
condition.await();
} catch (InterruptedException ex) {
//oh well
}
}
System.out.println(data);
} finally {
lock.unlock();
}
}
}
private static class Writer implements Runnable {
private final Lock lock;
private final Condition condition;
public Writer(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
lock.lock();
try {
data = 42;
ready = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) throws InterruptedException {
final ExecutorService executorService = Executors.newFixedThreadPool(2);
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
executorService.execute(new Reader(lock, condition));
executorService.execute(new Writer(lock, condition));
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.