Java 同步和内存可见性。 10 对其他线程可见吗?
Posted
技术标签:
【中文标题】Java 同步和内存可见性。 10 对其他线程可见吗?【英文标题】:Java synchronization and memory visibility. Is 10 visible to other threads? 【发布时间】:2017-03-18 05:13:32 【问题描述】:我有两节课。
一个类只是一个带有 get 和 set 同步方法的整数值的容器。
public class Sync
private int dataSync;
public synchronized int getDataSync()
return dataSync;
public synchronized void setDataSync(int data)
dataSync = data;
其他类类似。 它只是一个带有 get 和 set 方法的整数值的容器,没有同步。
public class NotSync
private int dataNotSync;
public int getDataNotSync()
return dataNotSync;
public void setDataNotSync(int data)
dataNotSync = data;
现在我的问题是“10 值保证对所有其他线程可见”在运行方法结束时。
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 类中的这个方法怎么样
public synchronized void run2()
dataNS.setDataNotSync(10);
run2 退出时,什么都不能保证刷新到主存?一个明确的答案将回答我的问题。如果否,则意味着只有锁的成员才能保证被刷新,并且如果其他线程在 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;
【问题讨论】:
为什么你的问题是反问的? 它对其他线程可见,这些线程在读取之前同步某些内容。 @Matt Timmermans 修辞,因为这只是一个例子。同步退出时,究竟是什么刷新回主内存?只有锁定成员?不是他们的内容? 如果这是一个反问的问题,那么您就不会期望得到答案,那么您一开始就不应该在这里发布它。你到底在问什么? @Mark Rotteveel 哈哈,是的,我的错误.. 对 rethorical 这个词感到困惑。刚刚检查了意思哎呀。只是是或否,并在代码注释中对问题 A 进行简短解释 【参考方案1】:答案是否定的,不保证可见,但可以。由于 10 的写入是不同步的,因此对于同步读取而言,在排序之前不会发生任何事情。
根据Java内存模型“一个线程对字段所做的更改保证仅在某些条件下对其他线程可见。
如果一个线程的写入发生在同步操作之前,这是正确的。对 10 的写入发生在之后,因此没有可见性保证。
例如,如果您有一个新字段并在写入 dataSync
(同步)之前将其写入为 10,并且您将 dataSync 评估为 45,那么对新字段的写入将是可见的。
【讨论】:
实际答案是“也许”,而不是“否”;) 应该说不保证可见,谢谢指正:) 当 setDataNotSync 退出时。我完全同意。 同步 void run() 何时退出? 是的,这行得通,尽管它可以在没有synchronized
的情况下工作。 volatile
写操作就像 synchronized
写然后读。因此,was10Written
存储之前的所有写入都被认为发生在was10Written
的读取之前。你得到的是,如果was10Written
为真,则getDataNotSync
保证为 10【参考方案2】:
答案是RunSync类中原始代码中dataNS.dataNotSync数据成员中的值10肯定会被任何其他在rs对象上同步的线程可见,并且在主线程获得rs的锁之后从 rs.run() 方法中退出。 dataNS.setDataNotSync(10);调用是在同步块内(在对象 rs 上)进行的,因此 JMM 在释放 rs 锁之前对主线程所做的任何变量所做的任何写入都将可见(之前发生过)任何线程随后获得 rs 上的锁,然后读取这些变量。
请注意,任何线程在主线程进行更改时只是休眠,然后尝试读取 dataNS 的值而不获得 rs 上的锁,不能保证看到值 10,因为没有发生 -在它们之间建立读取 dataNS 和写入 dataNS 的主线程之间的关系之前。
例如,在下面完成的 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,我们需要在线程的 run() 中编写一个“while(cond) rs.wait()”语句,这将保证我们不会在 rs.run() 从主线程完成执行之前读取 dataNS 值,并且我们需要调用 rs.notify() 作为 rs.run() 方法的最后一条语句。
【讨论】:
【参考方案3】:还有哪些线程?无论如何,您应该看到关键字 volatile 开始。此外,同步一种方法不会同步实例变量以进行读/写。因此,如果一个线程正在写入,另一个线程可能正在读取不同的值,因为只有方法是同步的(一次只有一个线程在设置值,而一个线程正在读取,但您可以同时在同一时间
【讨论】:
这是一个反问。想象还有其他线程。这只是说明问题的一个例子。以上是关于Java 同步和内存可见性。 10 对其他线程可见吗?的主要内容,如果未能解决你的问题,请参考以下文章