线程有自己的数据副本?
Thread has its own copy of data?
我在某处读到每个线程都有自己的共享状态副本,即使我使用同步或锁来修改变量,是什么保证更改的状态将被刷新到主内存而不是线程自己的缓存内存。
我知道 volatile 保证并证明了上述情况,即使我知道 synchronized 也证明了。
synchronized 如何保证更改值发生在主内存而不是线程缓存中。
前线程 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
如果不使用 synchronized
关键字(或 volatile 关键字),则无法保证当一个线程更改与其他线程共享的变量的值时,其他线程可以看到更改后的值。无法保证一个线程保存在 CPU 寄存器中的变量何时 "committed" 到主内存,并且无法保证其他线程何时 "refresh" 保存在 "refresh" 中的变量 CPU 从主存中注册。
使用 synchronized
关键字,当线程进入同步块时,它将刷新线程可见的所有变量的值(在同一个 lock/object 上)。当线程退出同步块时,对线程可见的变量的所有更改以及对于同一个锁将 pushed/updated 到主内存。
它实际上与volatile
工作相同。
在考虑 java 中的多线程代码时,您必须根据 happens-before
进行推理,这就是 JLS
使用的,这就是您应该使用的。期间。
以你的例子为例,你把 volatile
和 synchronized
放在一起,就好像他们在做同样的事情,有点——他们没有。即使您的示例已损坏,为了让 "other" 线程看到有保证的 a = 5
它 必须 在 相同的锁 上同步,你没有。 jcstress 测试证明你错了(我会让你弄清楚如何 运行 这正是)
@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;
}
}
即使看不懂代码,主要"selling point"是这样的:
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "SURPRISE")
您可以将其解读为:"while you were in the synchronized(lock){....}, some other thread came and read x and y"。那个阅读线程看到 1, 0
(x = 1
, y = 0
) 现在想想。你在 synchronized
块中,为什么一些线程读取 x = 1
和 y = 0
,你不是 "protected" 吗?不,你不是。如果你 运行 这个 - 你会得到 1, 0
。
编辑以回答评论
你 认为 你正在保护对 x
和 y
的写入 - 但由于 JLS
没有做出这样的保证,这是你的对事物的理解,这是错误的。就如此容易。您实际获得的唯一保护是如果您的作者 和 reader 使用相同的锁。
优化器可能 "see" 您仅在方法内部使用 lock
,因此将该代码转换为(至少在理论上):
@Actor
public void actor1() {
Object lock = new Object(); // < -- make lock local
synchronized (lock) {
x = 1;
y = 1;
}
}
既然 lock
现在是方法的本地方法,那么拥有它有什么意义呢?没有人可以访问它,并且可以完全将其删除。因此,您将获得执行两次独立写入的完全不受保护的代码。
结论是:您没有遵守 JLS
给您的规则 - 准备好得到奇怪的结果。
我在某处读到每个线程都有自己的共享状态副本,即使我使用同步或锁来修改变量,是什么保证更改的状态将被刷新到主内存而不是线程自己的缓存内存。
我知道 volatile 保证并证明了上述情况,即使我知道 synchronized 也证明了。
synchronized 如何保证更改值发生在主内存而不是线程缓存中。
前线程 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
如果不使用 synchronized
关键字(或 volatile 关键字),则无法保证当一个线程更改与其他线程共享的变量的值时,其他线程可以看到更改后的值。无法保证一个线程保存在 CPU 寄存器中的变量何时 "committed" 到主内存,并且无法保证其他线程何时 "refresh" 保存在 "refresh" 中的变量 CPU 从主存中注册。
使用 synchronized
关键字,当线程进入同步块时,它将刷新线程可见的所有变量的值(在同一个 lock/object 上)。当线程退出同步块时,对线程可见的变量的所有更改以及对于同一个锁将 pushed/updated 到主内存。
它实际上与volatile
工作相同。
在考虑 java 中的多线程代码时,您必须根据 happens-before
进行推理,这就是 JLS
使用的,这就是您应该使用的。期间。
以你的例子为例,你把 volatile
和 synchronized
放在一起,就好像他们在做同样的事情,有点——他们没有。即使您的示例已损坏,为了让 "other" 线程看到有保证的 a = 5
它 必须 在 相同的锁 上同步,你没有。 jcstress 测试证明你错了(我会让你弄清楚如何 运行 这正是)
@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;
}
}
即使看不懂代码,主要"selling point"是这样的:
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "SURPRISE")
您可以将其解读为:"while you were in the synchronized(lock){....}, some other thread came and read x and y"。那个阅读线程看到 1, 0
(x = 1
, y = 0
) 现在想想。你在 synchronized
块中,为什么一些线程读取 x = 1
和 y = 0
,你不是 "protected" 吗?不,你不是。如果你 运行 这个 - 你会得到 1, 0
。
编辑以回答评论
你 认为 你正在保护对 x
和 y
的写入 - 但由于 JLS
没有做出这样的保证,这是你的对事物的理解,这是错误的。就如此容易。您实际获得的唯一保护是如果您的作者 和 reader 使用相同的锁。
优化器可能 "see" 您仅在方法内部使用 lock
,因此将该代码转换为(至少在理论上):
@Actor
public void actor1() {
Object lock = new Object(); // < -- make lock local
synchronized (lock) {
x = 1;
y = 1;
}
}
既然 lock
现在是方法的本地方法,那么拥有它有什么意义呢?没有人可以访问它,并且可以完全将其删除。因此,您将获得执行两次独立写入的完全不受保护的代码。
结论是:您没有遵守 JLS
给您的规则 - 准备好得到奇怪的结果。