Synchronized data read/write to/from main memory
Asked Answered
A

4

5

When a synchronized method is completed, will it push only the data modified by it to main memory, or all the member variables, similarly when a synchronized method executes, will it read only the data it needs from main memory or will it clear all the member variables in the cache and read their values from main memory ? For example

public class SharedData
{

    int a; int b; int c; int d;

    public SharedData()
    {
        a = b = c = d = 10;
    }

    public synchronized void compute()
    {
        a = b * 20;
        b = a + 10;
    }

    public synchronized int getResult()
    {
        return b*c;
    }

}

In the above code assume compute is executed by threadA and getResult is executed by threadB. After the execution of compute, will threadA update main memory with a and b or will it update a,b,c and d. And before executing getResult will threadB get only the value of b and c from main memory or will it clear the cache and fetch values for all member variables a,b,c and d ?

Aerodonetics answered 31/7, 2012 at 5:27 Comment(0)
H
3

synchronized ensures you have a consistent view of the data. This means you will read the latest value and other caches will get the latest value. Caches are smart enough to talk to each other via a special bus (not something required by the JLS, but allowed) This bus means that it doesn't have to touch main memory to get a consistent view.

Harmonize answered 31/7, 2012 at 7:16 Comment(7)
thanks. Then why we need volatile key word in Java ? Please check this link linkAerodonetics
If you only use synchronized, you wouldn't need volatile. Volatile is useful if you have a very simple operation for which synchronized would be overkill.Harmonize
@PeterLawrey Sir, I have some related doubt abput synchronized block , Would be really helpful if you could enlighten me about my question : https://mcmap.net/q/901487/-do-we-need-to-synchronize-writes-if-we-are-synchronizing-reads/504133Eikon
@Eikon The answer to your question is; yes. synchronized reads don't mean anything without synchronized writes.Harmonize
@PeterLawrey Thanks , I feel I have understood, at least for the time being, will trouble more with further questions If I fail to discover the answers :) Thanks a lot for the help ..Eikon
@PeterLawrey "synchronized ensures you have a consistent view of the data. " so is the read barrier ensures to read the data even though the synchronize has been done on different object ? eg. : public class SharedData { int a; int b; int c; int d; public SharedData(Object x) { synchronized (x){....}; // is a,b,c,d are consistent here ? }Horologist
@MohammadKarmi consistent for read only, yes. you can't do read/write atomically if you use more than one lock. i.e. you get no more guarantee than volatile.Harmonize
A
2

I think following thread should answer your question.

Memory effects of synchronization in Java

In practice, the whole cache is not flushed.

Andino answered 31/7, 2012 at 5:56 Comment(2)
thanks I went through the link given by you. They have concluded that full flush will not happen. But its based on their assumption, they don’t refer to any proof. Is their any jvm spec which confirms full cache flush will not happen ?Aerodonetics
Apologies for late reply. JVM specs just define a contract. The implementations normally don't provide details. Lastly, its expensive to flush the whole cache. Therefore, logically, implementations go with optimizations where ever possible.Andino
A
1

1. synchronized keyword on a method or on an atomic statement, will lock the access to the resource that it can modify, by allowing only one thread to gain the lock.

2. Now preventing of caching of values into the variables is done by volatile keyword. Using volatile keyword will ask the JVM to make the thread that access the instance variable to reconcile its copy of the instance variable with the one saved in the memory.

3. Moreover in your above example, if threadA execute the compute(), then threadB canNot access the getResult() method simultaneously, as they both are synchronized methods, and only one thread can have access to the all the synchronized methods of the object, cause its not the method that is locked but the Object. Its like this... Every object has one lock, and the thread which wants to access its synchronized block must get that lock

4. Even every class has a lock, that is used to protect the crucial state of the static variables in the class.

Altimeter answered 31/7, 2012 at 5:32 Comment(3)
my question was not regarding lock, my question was regarding the memory model, how the values are getting sync with main memory from cache during synchronized method execution.Aerodonetics
During the synchronized process there is no sync mechanism...its simply like this.. When A thread alters the state of an crucial data, no other thread can access that data, once thread A is done with and the lock is released, then the other thread B can read it and write it, and at this time thread A canNot access that state of the data. Cache prevention mechanism is done when volatile keyword is used... thats what i tried to explain in my answer....Altimeter
thanks. Assume the code is running in multiprocessor environment, and threadA is executing in processor 1 and threadB is executing in processor 2. Assume both threads have cached the SharedData instance (as in above example) locally in processor cache. At time t1, threadA executes compute() method and releases the lock. After some time t1+delta (no lock on the instance), threadB executes getResult() method.Now threadB needs to refresh the processor cache for the member values of SharedData instance before executing the getResult(). What member variables gets refreshed (b&c or all) ?Aerodonetics
D
-1

Before answering your question lets clear few terms related to Multi-Threaded environments to understand the basic things.

  1. Race-Condition : When two or more thread trying to perform read or write operations on same variable on same time (here same variable = shared data between thread) eg. In your question Thraed-A execute b = a + 10 which is write operation on b and At same time Thread-B can execute b*c which is read operation on b. So here race condition is happening. We can handle race condition by using two ways one is by using Synchronized method or block and second by using Volatile Keyword.

  2. Volatile : Volatile keyword in Java guarantees that the value of the volatile variable will always be read from the main memory and not from Thread’s local cache. Normal variable without Volatile keyword is temporarily stored in Local cache for quick access and easy read write operations. Volatile doesn't block your thread it just make sure the write and read operation is perform in the sync. In the context of your example we can avoid race condition by making all variable as volatile

  3. Synchronized : Synchronization is achived by blocking of thread. It means it use lock and key mechanism in such a way that only one thread can execute this block of code at a time. So Thread-B is waiting in the doors of syncronized block until Thread-A finish completely and release key. if you use syncronized in front of static method then lock is considered on your class (.class) and if method is non static or instance method (same as your case) at that time lock is considered on instance of class or current object read

Now come to the point lets modify your example with few print statements and kotlin code

class SharedData {
    var a: Int
    var b: Int
    var c: Int
    var d = 10

    init {
        c = d
        b = c
        a = b
    }
    @Synchronized
    fun compute() : Pair<Int,Int>{
        a = b * 20
        b = a + 10
       return a to b
    }

    @Synchronized
    fun getComputationResult() : Int{
       return b * c
    }
}


@Test
fun testInstanceNotShared (){
    println("Instance Not Shared Example")
    val threadA = Thread{
        val pair = SharedData().compute()
        println("Running inside  ${Thread.currentThread().name}  compute And get  A = ${pair.first}, B = ${pair.second}  ")
    }
    threadA.name = "threadA"
    threadA.start()

    val threadB = Thread{
        println("Running inside  ${Thread.currentThread().name}  getComputationResult = ${SharedData().getComputationResult()}")
    }
    threadB.name = "threadB"
    threadB.start()

    threadA.join()
    threadB.join()

}

    // Output
//Instance Not Shared Example
//Running inside  threadB  getComputationResult = 100
//Running inside  threadA  compute And get  A = 200, B = 210


@Test
fun testInstanceShared (){
    println("Instance Shared Example")
    val sharedInstance = SharedData()
    val threadA = Thread{
        val pair = sharedInstance.compute()
        println("Running inside  ${Thread.currentThread().name}  compute And get  A = ${pair.first}, B = ${pair.second}  ")
    }
    threadA.name = "threadA"
    threadA.start()

    val threadB = Thread{
        println("Running inside  ${Thread.currentThread().name}  getComputationResult = ${sharedInstance.getComputationResult()}")
    }
    threadB.name = "threadB"
    threadB.start()

    threadA.join()
    threadB.join()
}

//Instance Shared Example
//Running inside  threadB  getComputationResult = 2100
//Running inside  threadA  compute And get  A = 200, B = 210
 

From above two test case you can identify that answer to your question is actually hidden in way how you call those methods (compute, getComputationResult) in multi-threaded environment.

After the execution of compute, will threadA update main memory

There is no guarantee that threadA Update the value of variable a,b,c,d on main memory but if you use Volatile keyword in front of those variable then it gives you 100% guarantee that those variable is updated with correct state immediately after modification happen

before executing getResult will threadB get only the value of b and c from main memory or will it clear the cache and fetch values for all member variables a,b,c and d

No

Addition to this - if you notice that in second test example even when two thread call the method same time you will get exact result. Means calling compute, getComputationResult same time still the getComputationResult method return the updated value from compute method this is because Synchronized and Volatile provide functionality called happens before which make sure that every write operations should be called before subsequent read operation.

Desperation answered 25/10, 2022 at 19:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.