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 对其他线程可见吗?的主要内容,如果未能解决你的问题,请参考以下文章

[Java并发编程实战] 共享对象之可见性

java中volatilesynchronized

可见性原子性有序性

java volatile

Java中Volatile关键字详解

java中volatile关键字的理解