Vladimir M.
Vladimir M.

Reputation: 1059

Happens-before with different monitors

Somewhere on this site (I don't remember the question) someone claimed that happens-before relashionship holds for different monitors and for different volatile variables, i.e.

// Thread T1
synchronized(O1)
{
}

// Thread T2
synchronized(O2)
{
}

If thread T2 enters synchronized(O2){} block after thread T1 leaves synchronized(O1){} block, T2 will see all changes made by T1. The explanation was that when a thread leaves synchronized block (or writes to volatile variable) it flushes it's cache into the memory. And when a thread enters synchronized block (or reads volatile variable) it discards it's cache and reads from the memory. Is it true?

Upvotes: 0

Views: 267

Answers (3)

acelent
acelent

Reputation: 8135

There is not a synchronizes-with relation between different monitors.

There is a happens-before relation between acquiring a lock and the following execution and a happens-before between an execution and the following lock release.

If one thread enters a synchronized block after another thread leaves a synchronized block, there might be a happens-before relation between the execution of the synchronized block on the earlier thread and the execution of the synchronized block on the latter thread.

You can only know for sure if or when the synchronized blocks don't run concurrently with a synchronizes-with relation. Without such relation, you have a data race.

If the code you show is all there is between the threads, then there is no synchronizes-with relation, and thus no happens-before relation. The rest of the answer shows cases where there is more code than the one provided in the question.

One thing that establishes a synchronizes-with relation is joining a thread, such as waiting for the former thread to finish. Another is to start a thread, such as starting the latter thread after the former finished.

// In a thread
t1.start();
t1.join();
t2.start();

// Or in thread 1
synchronized (O1) {
    // actions in thread 1
}
t2.start();

// Or in thread 2
t1.join();
synchronized (O2) {
    // actions in thread 2
}

Or if the latter thread is acquiring an outer lock owned by the former thread (that is eventually released):

// in thread 1
synchronized (outerLock) {
    synchronized (O1) {
        // actions in thread 1
    }
}

// in thread 2
synchronized (outerLock) {
    synchronized (O2) {
        // actions in thread 2
    }
}

Or if the latter thread is waiting on a lock (eventually) verifiably notified by the former thread:

boolean done;

// in thread 1
synchronized (O1) {
    // actions in thread 1
}
synchronized (commonLock) {
    done = true;
    commonLock.notify();
}

// in thread 2
synchronized (commonLock) {
    while (!done) {
        commonLock.wait();
    }
}
synchronized (O2) {
    // actions in thread 2
}

Or if the latter thread reads a volatile variable written by the former thread:

volatile boolean done;

// in thread 1
synchronized (O1) {
    // actions in thread 1
}
done = true;

// in thread 2
while (!done) {
    Thread.yield();
}
synchronized (O2) {
    // actions in thread 2
}

In this particular case, thread 2 only knows for sure it has read done after thread 1 has written to done if it observes a change.

Actually, all these cases have synchronizes-with relations, hence happens-before relations. The synchronization actions are the ones that guarantee a total order, so not all happens-before relations are between threads.

In Java Language Specification 8th Edition §17, you can read about synchronization actions:

A synchronization order is a total order over all of the synchronization actions of an execution.

And you can read the following about happens-before relation:

It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. If the reordering produces results consistent with a legal execution, it is not illegal.

This allows, in a single thread, non-volatile reads to be reordered as far behind as after the latest acquire and non-volatile writes to be reordered up to before the next release.

So, if you take something out of this:

  • The synchronizes-with relation is about multiple threads

  • Not all happens-before relations are about multiple threads

Upvotes: 0

user2982130
user2982130

Reputation:

Memory effects as a result of using synchronized or volatile are simply just that - memory effects. The fact that memory is observed to be "flushed from cache" has no direct relationship back to synchronized or volatile. Happens-before relationships are only true when operations occur as a result of actions specified in JLS 17.4.5, or as a result of being in a happens-before chain (i.e. piggyback) with those variables. If you perform two synchronized or volatile actions on different variables, there is NO happens-before relationship.

Memory effects come as a result of happens-before ordering, but happens-before never comes as a result of memory effects.

Upvotes: 2

Aman Goyal
Aman Goyal

Reputation: 393

Happens before relationship is true for volatile variable. In case of synchronized block happens before relationship is maintained in case you are taking the lock on same object. In your case synchronized is used on different object, that means 2 different threads can enter in the two different synchronized block. In this case, java memory model doesn't guarantee happens before relationship.

Upvotes: 0

Related Questions