Java易失性和同步

时间:2015-07-18 11:17:37

标签: java multithreading concurrency synchronized volatile

  1. 我知道volatile关键字会刷新所有不可见的数据,即如果某个线程读取volatile变量所有潜在的不可见变量/引用(不​​仅是将要读取的变量)将是okey(可见,即具有正确的值)之后这个阅读。对?但同步怎么样?它是一样的吗?如果在synchronized块中我们读取了3个变量,例如所有其他变量是否可见?

  2. 如果一个线程将某个变量的值(例如set varible“age”从2更改为33)从非同步块更改并且此线程死后,会有什么问题?该值可以写在线程堆栈中,但主线程可能不会看到此更改,后台线程将死亡,新值将消失且无法检索?

  3. 最后一个问题,如果我们有2个后台线程,并且我们知道我们的主线程将在其中每个人死亡之前通知(以某种方式),我们的线程将等待他们两个完成他们的工作并且将在此之后继续,我们如何确保所有变量(由后台线程进行)将对主线程可见?我们可以在后台线程完成后放置synchronized块或?我们不希望每次在此线程死亡之后访问使用synchronized块的后台线程更改的变量(因为它是开销),但是我们需要拥有正确的值?但是读取一些虚假的volatile变量或使用伪同步块(如果它刷新所有数据)只是为了刷新所有数据是不自然的。

  4. 我希望我的问题得到很好的解释。 提前谢谢。

3 个答案:

答案 0 :(得分:4)

读取volatile变量的值会在一个线程的写入和另一个线程的读取之间创建一个先发生的关系。

请参阅http://jeremymanson.blogspot.co.uk/2008/11/what-volatile-means-in-java.html

  

Java volatile修饰符是一个特殊机制的示例   保证线程之间的通信发生。当一个线程   写入一个volatile变量,另一个线程看到写入,   第一个线程告诉第二个关于内存的所有内容   直到它执行对该volatile变量的写入。

同步块也会创建一个先发生过的关系。见http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html

  

监视器的解锁(同步块或方法退出)   发生在每个后续锁定之前(同步块或方法)   那个监视器的入口)。而且因为之前发生的关系   是传递的,解锁之前线程的所有动作   发生 - 在任何线程锁定之后的所有操作之前发生   监视。

对可见性有相同的影响。

如果在没有任何同步的情况下写入值,则无法保证其他线程将看到此值。如果要跨线程共享值,则应添加某种同步或锁定,或使用volatile。

答案 1 :(得分:4)

您的所有问题都在the java.util.concurrent package documentation中解答。

  
      
  1. 但是同步呢?
  2.   

以下是文档说的内容:

  

监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法条目)之前。并且由于之前发生的关系是可传递的,因此在解锁之前线程的所有操作都会发生 - 在任何线程锁定该监视器之后的所有操作之前。

  
      
  1. 这个值可以写在线程堆栈中,但主线程可能不会看到这个更改,后台线程会死掉,新值就消失了,无法检索?
  2.   

如果值写在线程堆栈中,那么我们讨论的是局部变量。除了声明该变量的方法之外,局部变量在任何地方都不可访问。因此,如果线程死亡,当然堆栈不存在,并且局部变量也不存在。如果你在谈论一个对象的字段,那就存储在堆上。如果没有其他线程具有对此对象的引用,并且如果无法从任何静态变量访问引用,那么它将被垃圾收集。

  
      
  1. 我们的线程将等待他们两个完成他们的工作并在此之后继续,我们如何确保所有变量(由后台线程进行)将对主线程可见
  2.   

文档说:

  

线程中的所有操作都会发生 - 在任何其他线程从该线程的连接成功返回之前。

因此,由于主线程等待后台线程死亡,它使用join(),并且在join()返回后主线程可以看到后台线程所做的每个操作。

答案 2 :(得分:1)

从您的问题似乎暗示的整个主题完全涵盖整个主题需要的不仅仅是StackOverflow.com的答案,所以我建议寻找一本关于多线程编程的好书

volatile保证对相同volatile变量 1 的其他访问完全对有限变量的读写访问进行排序。
它通过阻止volatile读取和写入访问与先前或将来的指令重新排序并强制执行写入线程访问之前的所有副作用对读取线程可见来实现此目的。

这意味着你可以像在代码中看到的那样读取和写入volatile变量,就像一次一个地执行指令一样,只有当前一个的所有副作用都完成并且对每个其他线程都可见时才开始下一个。

为了更好地理解这意味着什么以及为什么这是必要的,请在difference between Memory Barriers and lock prefixed instruction上查看我的这个问题。

注意,Java中的volatile比C或C ++中的volatile 强得多。它确实保证了读/写访问的通常处理作为优化目的的副作用。这意味着并不是简单地暗示每次都从内存中读取变量,Java volatile是一个内存障碍。

synchronized块只保证执行一段代码的独占(即一次一个线程) 它并不意味着所有线程都看到内存访问是相同的顺序,一个线程首先可以看到写入受保护的共享数据结构,然后写入锁!

1 在某些情况下,在发出完整内存屏障的情况下,这可能会强制对T中其他线程可见的对线程T所做的所有volatile变量的写入和读取程序顺序。请注意,这不足以进行同步,因为线程间访问之间仍然没有关系。

  1. 没有

  2. 共享变量不在任何线程的堆栈中,其值的副本可以是但变量独立于任何线程存在。
    当线程正常死亡时,写入完成并可以检索(再次注意内存排序) 如果线程强行死亡,它可能会在任何地方中断。无论如何,如果线程在写入着色var之前停止(通过直接赋值或通过复制堆栈上的本地值),则实现实际的Java赋值,然后写入永远不会发生。

  3. 您不需要synchronized,只需要volatile,因为主线程只读取,后台线程只写(不同的变量)。

相关问题