Java 同步和内存可见性。 10对其他线程可见吗?
Java synchronization and memory visibility. Is 10 visible to other threads?
我有两个 class。
一个 class 只是一个整数值的容器,具有 get 和 set 同步方法。
public class Sync {
private int dataSync;
public synchronized int getDataSync() {
return dataSync;
}
public synchronized void setDataSync(int data) {
dataSync = data;
}
}
其他class也是类似的。
它只是一个整数值的容器,具有不同步的 get 和 set 方法。
public class NotSync {
private int dataNotSync;
public int getDataNotSync() {
return dataNotSync;
}
public void setDataNotSync(int data) {
dataNotSync = data;
}
}
现在我的问题是 运行 方法末尾的 "is 10 value guaranteed to be visible to all other threads"。
public class RunSync {
public static void main(String[] args) {
RunSync rs = new RunSync();
rs.run();
}
private NotSync dataNS;
private Sync dataS;
private int data;
public RunSync() {
dataS = new Sync();
dataNS = new NotSync();
}
public synchronized void run() {
data = 100;
dataS.setDataSync(45);
dataNS.setDataNotSync(10);
//Question A: is 10 value guaranteed to be visible to all other
//threads when method exits?
//we are inside a synchronized block aren't we?
//so all writes must be flushed to main memory
}
}
编辑:假设还有其他线程。这只是一个快速编写的示例。问题是当同步块完成时,什么能完全保证刷新回主内存。
EDIT2:根据java内存模型
“只有在某些情况下,一个线程对字段所做的更改才能保证对其他线程可见。一种情况是写入线程释放同步锁,读取线程随后获得相同的同步锁。
那么如果另一个线程获取了RunSync锁,是否能保证在RunSync的NotSync实例中看到10?即使 NotSync 不受保护?
Edit3:没有明确答案的相关问题。我还在找。
What is the scope of memory flushed or published to various threads when using volatile and synchronized?
EDIT4:为了简化示例,RunSync class
中的这个方法怎么样?
public synchronized void run2() {
dataNS.setDataNotSync(10);
}
当运行2 退出时,什么都不能保证刷新到主内存?对此的明确回答将回答我的问题。如果否,这意味着只有锁的成员才能保证被刷新,并且如果其他线程在 RunSync 上获得相同的锁,则其他线程将可见。
答案是不保证。
EDIT5:在这种情况下,断言是否保证为真?
public class RunSync {
public volatile boolean was10Written = false;
public synchronized void run2() {
dataNS.setDataNotSync(10);
was10Written = true;
}
public void insideAnotherThread() {
if(was10Written) {
int value = dataNS.getDataNotSync();
assert value == 10;
}
}
}
还有哪些话题?无论如何,您应该看到关键字 volatile 作为开始。同样,同步一个方法不会同步 read/write 的实例变量。因此,如果一个线程正在写入,另一个线程可以读取不同的值,因为只有方法是同步的(一次只有一个线程正在设置值,一个正在读取,但你可以同时做 read/write同时
答案是否定的,不能保证它是可见的,但它可能是。由于 10 的写入未同步,因此在同步读取的排序之前没有发生任何事情。
According to the java memory model "changes to fields made by one thread are guaranteed to be visible to other threads only under some conditions.
如果一个线程的写入发生在同步操作之前,则为真。写入 10 发生在之后,因此没有可见性保证。
例如,如果您有一个新字段并在写入 dataSync
(同步)之前将其写入 10,并且您评估 dataSync 为 45,那么写入新字段将可见。
答案是classRunSync中原代码中dataNS.dataNotSync数据成员中的值10肯定会被任何其他在rs对象上同步的线程可见,并获得在主线程退出 rs.run() 方法后锁定 rs。 dataNS.setDataNotSync(10);调用是在一个同步块内进行的(在对象 rs 上),因此由 JMM,在 rs 锁释放之前对主线程所做的任何变量的任何写入,都将变得可见(之前发生过)任何线程随后获得 rs 上的锁,然后读取这些变量。
请注意,任何在主线程进行更改时处于休眠状态的线程,然后尝试读取 dataNS 的值而不获取 rs 上的锁,都不能保证看到值 10,因为没有在它们读取 dataNS 和主线程写入 dataNS 之间建立了 happens-before 关系。
例如,在下面完成的 main() 方法中,线程 t1 保证打印 10,但这是因为我们在 rs.run() 代码执行后启动线程 t1;如果我们替换 rs.run() 的顺序;和 t1.start();仅当 t1 在 rs.run() 获得锁(并完成)后获得锁时才会打印值 10:
public static void main(String[] args) {
RunSync rs = new RunSync();
Thread t1 = new Thread() {
public void run() {
synchronized(rs) {
System.out.println(rs.dataNS.getDataNotSync());
}
}
};
rs.run();
t1.start();
}
如果在上面的代码中我们写“t1.start();”然后跟着“rs.run()”,为了让t1保证打印10,我们需要在[=33=中写一个“while(cond) rs.wait()”语句]() 我们的线程,这将保证我们在 rs.run() 完成从主线程执行之前不读取 dataNS 值,并且我们需要调用 rs.notify() 作为最后一个rs.run() 方法的语句。
我有两个 class。
一个 class 只是一个整数值的容器,具有 get 和 set 同步方法。
public class Sync {
private int dataSync;
public synchronized int getDataSync() {
return dataSync;
}
public synchronized void setDataSync(int data) {
dataSync = data;
}
}
其他class也是类似的。 它只是一个整数值的容器,具有不同步的 get 和 set 方法。
public class NotSync {
private int dataNotSync;
public int getDataNotSync() {
return dataNotSync;
}
public void setDataNotSync(int data) {
dataNotSync = data;
}
}
现在我的问题是 运行 方法末尾的 "is 10 value guaranteed to be visible to all other threads"。
public class RunSync {
public static void main(String[] args) {
RunSync rs = new RunSync();
rs.run();
}
private NotSync dataNS;
private Sync dataS;
private int data;
public RunSync() {
dataS = new Sync();
dataNS = new NotSync();
}
public synchronized void run() {
data = 100;
dataS.setDataSync(45);
dataNS.setDataNotSync(10);
//Question A: is 10 value guaranteed to be visible to all other
//threads when method exits?
//we are inside a synchronized block aren't we?
//so all writes must be flushed to main memory
}
}
编辑:假设还有其他线程。这只是一个快速编写的示例。问题是当同步块完成时,什么能完全保证刷新回主内存。
EDIT2:根据java内存模型 “只有在某些情况下,一个线程对字段所做的更改才能保证对其他线程可见。一种情况是写入线程释放同步锁,读取线程随后获得相同的同步锁。
那么如果另一个线程获取了RunSync锁,是否能保证在RunSync的NotSync实例中看到10?即使 NotSync 不受保护?
Edit3:没有明确答案的相关问题。我还在找。 What is the scope of memory flushed or published to various threads when using volatile and synchronized?
EDIT4:为了简化示例,RunSync class
中的这个方法怎么样?public synchronized void run2() {
dataNS.setDataNotSync(10);
}
当运行2 退出时,什么都不能保证刷新到主内存?对此的明确回答将回答我的问题。如果否,这意味着只有锁的成员才能保证被刷新,并且如果其他线程在 RunSync 上获得相同的锁,则其他线程将可见。 答案是不保证。
EDIT5:在这种情况下,断言是否保证为真?
public class RunSync {
public volatile boolean was10Written = false;
public synchronized void run2() {
dataNS.setDataNotSync(10);
was10Written = true;
}
public void insideAnotherThread() {
if(was10Written) {
int value = dataNS.getDataNotSync();
assert value == 10;
}
}
}
还有哪些话题?无论如何,您应该看到关键字 volatile 作为开始。同样,同步一个方法不会同步 read/write 的实例变量。因此,如果一个线程正在写入,另一个线程可以读取不同的值,因为只有方法是同步的(一次只有一个线程正在设置值,一个正在读取,但你可以同时做 read/write同时
答案是否定的,不能保证它是可见的,但它可能是。由于 10 的写入未同步,因此在同步读取的排序之前没有发生任何事情。
According to the java memory model "changes to fields made by one thread are guaranteed to be visible to other threads only under some conditions.
如果一个线程的写入发生在同步操作之前,则为真。写入 10 发生在之后,因此没有可见性保证。
例如,如果您有一个新字段并在写入 dataSync
(同步)之前将其写入 10,并且您评估 dataSync 为 45,那么写入新字段将可见。
答案是classRunSync中原代码中dataNS.dataNotSync数据成员中的值10肯定会被任何其他在rs对象上同步的线程可见,并获得在主线程退出 rs.run() 方法后锁定 rs。 dataNS.setDataNotSync(10);调用是在一个同步块内进行的(在对象 rs 上),因此由 JMM,在 rs 锁释放之前对主线程所做的任何变量的任何写入,都将变得可见(之前发生过)任何线程随后获得 rs 上的锁,然后读取这些变量。
请注意,任何在主线程进行更改时处于休眠状态的线程,然后尝试读取 dataNS 的值而不获取 rs 上的锁,都不能保证看到值 10,因为没有在它们读取 dataNS 和主线程写入 dataNS 之间建立了 happens-before 关系。
例如,在下面完成的 main() 方法中,线程 t1 保证打印 10,但这是因为我们在 rs.run() 代码执行后启动线程 t1;如果我们替换 rs.run() 的顺序;和 t1.start();仅当 t1 在 rs.run() 获得锁(并完成)后获得锁时才会打印值 10:
public static void main(String[] args) {
RunSync rs = new RunSync();
Thread t1 = new Thread() {
public void run() {
synchronized(rs) {
System.out.println(rs.dataNS.getDataNotSync());
}
}
};
rs.run();
t1.start();
}
如果在上面的代码中我们写“t1.start();”然后跟着“rs.run()”,为了让t1保证打印10,我们需要在[=33=中写一个“while(cond) rs.wait()”语句]() 我们的线程,这将保证我们在 rs.run() 完成从主线程执行之前不读取 dataNS 值,并且我们需要调用 rs.notify() 作为最后一个rs.run() 方法的语句。