【问题标题】:Memory consistancy in java.util.concurrentjava.util.concurrent 中的内存一致性
【发布时间】:2011-07-21 09:43:23
【问题描述】:

Memory Consistancy Property,我们知道: “在将对象放入任何并发集合之前线程中的操作发生在另一个线程中从集合中访问或删除该元素之后的操作。”

这是否意味着:如果我在一个线程中创建一个对象并将其放入 ConcurrentLinkedQueue 中,另一个线程将看到该对象的所有属性而无需对该对象进行其他同步?

例如:

public class Complex{

  int index;

  String name;

  public Complex(int index, String name){

     this.index = index;

     this.name = name;

  }

  public String getName(){

     return name;

  }

  public int getIndex(){

     return index;

  }

}

public class SharedQueue{

   public static ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();

}

在一个线程中:

...........

Complex complex = new Complex(12, "Sam");

SharedQueue.queue.add(complex);

...........

在另一个线程中

......

Complex complex = SharedQueue.queue.poll();

System.out.printly(complex.getIndex() + ";" + complex.getName());

......

第二个线程肯定会看到complex 对象的属性吗?如果第二个线程恰好在第一个线程将对象放入队列后获取对象并打印它。

我们知道,在正常情况下,如果对象是共享的,我们应该在多线程环境中同步对象。

喜欢

public class Complex{

  int index;

  String name;

  public Complex(int index, String name){

     this.index = index;

     this.name = name;

  }

  public synchronized String getName(){

     return name;

  }

  public synchronized int getIndex(){

     return index;

  }

}

【问题讨论】:

    标签: java memory consistency java.util.concurrent


    【解决方案1】:
    1. 所有线程都可以看到它们可以引用的所有对象,所以是的,这也适用于集合。并发集合使用同步不会因为两个或多个线程同时访问它们而中断(例如,for-loop 可以中断),因此您可以使用它们在线程之间共享对象。

    2. 如果您的对象是不可变的,即只读的,就像上面的 Complex 类(添加 final 修饰符),那么您不需要同步对它的访问,因为它是线程安全的。

      李>

    同步的重点是确保您的变量在 n 次操作中保持一致。示例:

    如果你想计算

    i = i + 2;
    

    那么这包括首先读取 i 的值,加 2,然后将值设置回 i。

    现在,如果在您添加 2 之后出现其他线程并更新 i,那么您实际上将覆盖此更新,因为您仍然持有基于先前值 + 2 的总和,并将其设置回 i。

    【讨论】:

    • Complex 不是不可变的,因为它的字段不是final。如果没有适当的同步,其他线程可以在部分构造状态下观察它。
    • 相应地更新了答案,希望他们有一天能杀死部分建设状态。
    【解决方案2】:

    没错。在这种情况下,ConcurrentLinkedQueue 用作同步器。

    【讨论】:

      【解决方案3】:

      这意味着如果线程 A 将一个对象放入集合中,并且线程 B 从那里获取它,线程 B 可以看到线程 A 在将对象放入集合之前所做的任何操作的结果(以及 happened-before 它们在其他线程中,等等)。这些动作包括对象的初始化(如果它是由线程 A 完成的),以便线程 B 可以看到处于一致状态的对象。

      请注意,它不保证在将对象放入集合后线程 A 中发生的操作或其他线程的操作(与传递 happens-before 顺序相关的操作除外,如如上所述),因此如果您要在将对象放入集合后对其进行修改,您仍然需要同步。

      顺便说一句,您的“正常情况”示例已损坏,因为构造函数中的操作不同步。

      【讨论】:

        【解决方案4】:

        JSR 133 还指定 Object 的 final 字段是完整的,并且在构造函数完成时对所有线程可见。 Java 5.0(2004 年)中添加了此支持。您的对象无需任何其他操作即可在所有线程中可见且正确。

        在这种情况下,在不可变对象上使用同步不会实现任何效果。

        有很多文件另有说明,这些文件通常早于 2004 年。;)

        【讨论】:

        • JLS 在哪里指定这个?一个著名的双重检查单例示例表明,如果没有适当的同步,其他线程可以观察到部分构造的对象。
        • @axtavt,抱歉不在 JLS 中,在 JSR 133 中。
        • JSR 133 是 JLS 3 的内存模型,但它不提供关于构造函数的任何保证(final 字段除外)。
        • @axtaxt,好点,我只使用 final 字段。我会将其添加到答案中。
        猜你喜欢
        • 2019-01-17
        • 2018-01-02
        • 1970-01-01
        • 2012-09-09
        • 2015-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-15
        相关资源
        最近更新 更多