Multi-threaded access and thread variable cache

I could find the answer if I read the full chapter / book on multithreading, but I would like to get a faster answer. (I know this stackoverflow question is similar, but not enough.)

Suppose this class exists:

public class TestClass { private int someValue; public int getSomeValue() { return someValue; } public void setSomeValue(int value) { someValue = value; } } 

There are two threads (A and B) that access an instance of this class. Consider the following sequence:

  • A: getSomeValue ()
  • B: setSomeValue ()
  • A: getSomeValue ()

If I'm right, someValue should be mutable, otherwise the 3rd step may not return the updated value (since A may have a cached value). Is it correct?

Second scenario:

  • B: setSomeValue ()
  • A: getSomeValue ()

In this case, A will always get the correct value, because this is its first access, so it cannot yet have a cached value. Is it correct?

If in the second case the class is accessed only , is there no need for volatile / synchronization, or is it?

Please note that this example has been simplified, and in fact I'm interested in learning about individual member variables and methods in a complex class, and not about entire classes (i.e. which variables should be volatile or have synchronized access). The main thing: if there are more streams of access to certain data, is synchronized access necessary in all ways or does it depend on the way of access (for example, order) to them?


After reading the comments, I will try to present the source of my confusion with another example:

  • From UI threadA.start() : threadA.start()
  • threadA calls getSomeValue() and reports the UI thread
  • The user interface thread receives the message (in the message queue), so it calls: threadB.start()
  • threadB calls setSomeValue() and reports the UI thread
  • the user interface thread receives a message and reports threadA (in some way, for example, a message queue)
  • threadA calls getSomeValue()

This is a fully synchronized structure, but why does this mean that threadA will get the most recent value in step 6? (if someValue not volatile or does not fit on the monitor when accessed from anywhere)

+6
source share
5 answers

The problem is that java is just a specification. There are many JVM implementations and examples of physical work environments. In any given combination, the action may be safe or dangerous. For example, on uniprocessor systems, the volatile keyword in your example is probably completely unnecessary. Since the authors of the memory and language specifications cannot reasonably take into account possible sets of working conditions, they choose white lists of certain templates that are guaranteed to work on all compatible implementations. Following these guidelines ensures that your code will work on your target system and that it will be portable enough.

In this case, "caching" usually refers to activity that occurs at the hardware level. There are certain events in java that trigger kernels on multiprocessor systems to "synchronize" their caches. An example of this is access to volatile variables; synchronized blocks are different. Imagine a scenario in which these two threads X and Y are scheduled to run on different processors.

 X starts and is scheduled on proc 1 y starts and is scheduled on proc 2 .. now you have two threads executing simultaneously to speed things up the processors check local caches before going to main memory because its expensive. x calls setSomeValue('x-value') //assuming proc 1 cache is empty the cache is set //this value is dropped on the bus to be flushed //to main memory //now all get will retrieve from cache instead //of engaging the memory bus to go to main memory y calls setSomeValue('y-value') //same thing happens for proc 2 //Now in this situation depending on to order in which things are scheduled and //what thread you are calling from calls to getSomeValue() may return 'x-value' or //'y-value. The results are completely unpredictable. 

The fact is that volatile (on compatible implementations) ensures that an ordered record will always be cleared to main memory, and other processor caches will be marked as dirty before the next access, regardless of the stream from which this access takes place.

disclaimer: changeable NOT BLOCKED. This is especially important in the following case:

 volatile int counter; public incrementSomeValue(){ counter++; // Bad thread juju - this is at least three instructions // read - increment - write // there is no guarantee that this operation is atomic } 

this may be relevant to your question if you intend for setSomeValue always be called before getSomeValue

If the goal is that getSomeValue() should always reflect the most recent call to setSomeValue() , then this is a good place to use the volatile keyword. Just remember that without it, there is no guarantee that getSomeValue() will reflect the most recent call to setSomeValue() , even if setSomeValue() was scheduled first.

+1
source

If two threads call the same methods, you cannot make any guarantees regarding the order in which these methods are called. Therefore, your initial package, which depends on the call order, is not valid.

It is not about the order in which methods are called; it's about syncing. It's about using some kind of mechanism to animate one stream, while the other has completed the write operation completely. After you decide to have more than one thread, you must provide this synchronization mechanism to avoid data corruption.

+3
source

As we all know, its key state of data, which we need to protect, and the atomic statements that determine the decisive state of data, must be synchronized.

I had this example where volatile is used, and then I used 2 threads, which were used to increase the counter by 1 each time to 10000. Thus, it should be a total of 20,000. But, to my surprise, it didnt always It was.

Then I used a synchronized keyword to make it work.

Synchronization ensures that when a thread accesses a synchronized method, no other thread is allowed to access this or any other synchronized method of this object , make sure that data corruption is not performed.

The Thread-Safe class means that it will maintain its correctness if there is planning and rotation of the Runtime runtime without restricting the flow from the client side that access this class.

+2
source

Take a look at the book.

A field can be declared mutable, in which case the Java memory model (Β§17) ensures that all threads will see the consistent value for the variable.

So, volatile is a guarantee that the declared variable will not be copied to the local thread storage, which is otherwise permitted. He also explained that this is a deliberate alternative to blocking for very simple types of synchronized access to shared storage.

Also see this earlier article , which explains that access to int necessarily atomic (but not double or long ).

This means that if your int field is declared volatile , then locks are not required to ensure atomicity: you will always see the value that was recently recorded in memory, and not some confused value obtained as a result of a half-complete record (like it is possible with double or long).

However, you seem to imply that your getters and setters themselves are atomic. This is not guaranteed. The JVM may interrupt execution at intermediate points during a sequence of calls or returns. In this example, this has no consequences. But if the calls had side effects like setSomeValue(++val) , then you will have a different story.

+2
source

If I'm right, someValue should be volatile, otherwise the 3rd step may not return the updated value (since A may have a cached value). Is it correct?

If thread B calls setSomeValue (), you need some kind of synchronization so that thread A can read this value. volatile will not do this on its own, and synchronization of these methods will also fail. The code that does this, ultimately, regardless of the synchronization code you added, made sure that A: getSomeValue() comes after B: setSomeValue() . If you think you used a message queue to synchronize threads, this is because the memory changes made by thread A became visible to thread B as soon as thread B acquired a lock on the message queue.

If access to the class is carried out only in the second way, is there no need for unstable / synchronization, or is it?

If you really do your own synchronization, then it doesn't sound as if you care about whether these classes are thread safe. Make sure you are not accessing them from multiple threads at the same time; otherwise, any methods that are not atomic (specifying int) can lead to an unpredictable state. One common pattern is to put the general state in an immutable object so that you are sure that the receiving stream does not call any setters.

If you have a class that you want to update and read from multiple threads, I will probably do the easiest thing to get started, which often synchronizes all public methods. If you really think this is a bottleneck, you can explore some of the more complex locking mechanisms in Java.

So what is a flying guarantee?

For exact semantics, you may need to read tutorials, but one way to summarize them is that 1) any memory changes made by the last thread to access volatile will be visible to the current thread accessing the volatile, and 2) that access to volatile is atomic (it will not be a partially constructed object, or a partially designated double or long).

Synchronized blocks have similar properties: 1) any memory changes made by the last thread to access the lock will be visible to this thread, and 2) changes made inside the block are performed atomically with respect to other synchronized blocks

(1) means any changes in memory, not just changes in the volatile (we say JDK 1.5 post) or inside a synchronized block. This is what people mean when they refer to ordering, and this is done differently on different chip architectures, often using memory barriers.

In addition, in the case of synchronous blocks (2) only guarantees that you will not see inconsistent values ​​if you are in another block synchronized with the same lock. It is generally recommended that you synchronize all access to shared variables if you really don't know what you are doing.

+1
source

Source: https://habr.com/ru/post/919361/


All Articles