0

I have read somewhere that every threads have its own copy of shared states, even if I'm using synchronized or locks for modifying the variables, what guarantees that the changed state will be flushed into the main memory rather than in thread's own cache memory.

I know volatile guarantees and justifies the above scenario, even I know synchronized justifies too.

How does synchronized guarantees that changing the value is happening in the main memory rather than thread cache memory.

Ex Thread 1

synchronized(this)
{
int a = 0;
a = 5;
}  ----> the value might got changed in thread's cache memory another thread entering the block could read a value as 0

volatilte int a = 0;
a = 5; ----> another executing thread will read a values as 5
Shelly
  • 183
  • 1
  • 1
  • 11
  • https://stackoverflow.com/questions/1850270/memory-effects-of-synchronization-in-java – Adithya Apr 15 '20 at 17:56
  • 3
    Note that @CodeScale 's answer does not use the phrase "own copy." Neither does the Java Language Specification (JLS). It also doesn't say "cache" or "main memory." The JLS describes an idealized, fictional Java machine, in which every variable exists in just one place. But when two or more threads share variables, the JLS only promises that they will "see" a consistent view of them if the program follows certain rules. Cached copies of variables are a real thing in any real Java implementation, but you should adopt the language of the JLS when deciding whether a program obeys its rules. – Solomon Slow Apr 15 '20 at 19:51

2 Answers2

4

You have to reason in terms of happens-before when thinking about multi-threaded code in java, this is what the JLS uses and this is what you should use. Period.

Going to your example, you munch volatile and synchronized together as if they do the same thing, sort of - they don't. Even your example is broken, for the "other" thread to see a guaranteed a = 5 it must synchronize on the same lock, which you don't. A jcstress test proves you wrong (I'll let you figure out how to run this exactly)

@JCStressTest
@State
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "SURPRISE")
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE, desc = "whatever")
@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE, desc = "whatever")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "whatever")
public class DifferentSynchronizedObjects {

    int x, y;
    private Object lock = new Object();

    @Actor
    public void actor1() {
        synchronized (lock) {
            x = 1;
            y = 1;
        }
    }

    @Actor
    public void actor2(II_Result r) {
        r.r1 = x;
        r.r2 = y;
    }
} 

Even if you do not understand the code, it's main "selling point" is this:

@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "SURPRISE")

You can read it as : "while you were in the synchronized(lock){....}, some other thread came and read x and y". That reading thread saw 1, 0 (x = 1, y = 0) and now think about it. You were in the synchronized block, how come some thread read x = 1 and y = 0, aren't you "protected"? No you are not. If you run this - you will get 1, 0.

EDIT to answer the comment

You think you are protecting both writes to x and y - but since the JLS makes no such guarantees, it's your understanding of things, which is wrong. As simple as that. The only protection you actually get is if your writer and reader would use the same lock.

An optimizer might "see" that you are using that lock only inside a method, and as such transform that code to (in theory at least):

@Actor
public void actor1() {

    Object lock = new Object(); // < --  make lock local       

    synchronized (lock) {
        x = 1;
        y = 1;
    }
}

Since the lock is now local to a method what is the point in having it at all? No one can access it, and simply elide it entirely. Thus you get a totally unprotected code that performs two independent writes.

The conclusion is : you do not follow the rules that the JLS gives you - be prepared to get weird results.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • It may be because the thread which is executing the synchronized block haven't reached to the statement ```y=1``` and at that time the other thread read the value of ```y as 0``` which is possible ```as per my limited knowledge```; but the other thread read the value of ```x = 1```; how synchronized block helps happens before relationship? by pushing the updates into the memory which is common to all the threads? or the ```cache coherency protocol will take care of happen before``` Little confused? – Shelly Apr 16 '20 at 08:45
  • @Shelly but isn't that Thread in a `synchronized` block? How can someone look in between those two writes while they are both protected by that lock? Do you see the irony here? – Eugene Apr 16 '20 at 11:07
  • ok! got it, but how this possible? ```thread 1 executes sync block; after that thread 2 enters the block``` how the output can be 1 and 0 over here happens before relationship is fulfilling right? – Shelly Apr 16 '20 at 11:24
  • I got what you are really trying to explain in the above context, there no advantage of having ```Object lock = new Object();``` statement in the method itself, however if all the threads locking on the same distinct object the results are guarantee right? Should I consider the other thread is able to see the updated results because of happen before relationship if yes? should I assume the thread not updating the value in their cache? so all threads are referencing to some common memory where all share object has been kept and all the changes are happening over there? – Shelly Apr 17 '20 at 09:19
  • 1
    @Shelly same _distinct_ Object? These are contrary terms. If you want you really meant "same", then yes: if both reading and writing threads do use the same lock, there will be the "happens-before" rule. How that happens internally is an implementation detail that you should not care ( its very complicated anyway). – Eugene Apr 17 '20 at 10:49
1

Without the use of the synchronized keyword (or volatile one) there is no guarantee that when one thread changes the value of a variable shared with other threads, that the other threads can see the changed value. There are no guarantees about when a variable kept in a CPU register by one thread is "committed" to main memory, and there is no guarantee about when other threads "refresh" a variable kept in a CPU register from main memory.

With synchronized keyword when a thread enters a sync block it will refresh the values of all variables visible to the thread(on the same lock/object). When a thread exits a synchronized block all changes to variables visible to the thread and for the same lock will be pushed/updated to main memory. It is actually the same as volatile works.

CodeScale
  • 3,046
  • 1
  • 12
  • 20
  • 1
    Visibility guarantees are only made for writes performed by a thread before leaving a synchronized block and a thread subsequently entering a synchronized block *for the same object*. Hence, there is no requirement to commit or re-read all variables, as only variables known by both threads are affected. And when the object used in synchronized(...) is not known by the other thread, no variable is affected at all. Further, making visible does not necessarily imply accessing the main memory. – Holger Apr 15 '20 at 21:26
  • 1
    _When a thread exits a synchronized block all changes to variables visible to the thread will be pushed/updated to main memory_: [this is wrong](https://stackoverflow.com/a/61239326/1059372). You need to be explicit that this is _for the same lock_ and also it is very rarely the case when something goes to main memory; in most general case cache coherency protocol will take care of this in different ways. – Eugene Apr 15 '20 at 22:00
  • Thanks for correction. About this `it is very rarely the case when something goes to main memory; in most general case cache coherency protocol will take care of this in different ways.` does it change the main idea that next thread entering the sync block will always see "fresh value" for the object/lock variable? – CodeScale Apr 16 '20 at 03:49