问题
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
回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/61235213/thread-has-its-own-copy-of-data