当前位置: 首页 > 面试题库 >

同步以确保对不可变对象的引用将被另一个线程看到

卓雅达
2023-03-14
问题内容

我正在对此进行研究,以了解新JMM(从5开始)中最终字段的行为。这个概念很明确:在正确构造对象之后,可以保证初始化的最终字段对所有线程的可见性。

但是,在本节的最后,我读到了这,这简直让我感到困惑:

综上所述,如果在线程构造了一个不变对象(即仅包含最终字段的对象)之后,您想要确保所有其他线程都能正确看到该对象,则通常仍然需要使用同步。没有其他html" target="_blank">方法可以确保,例如,第二个线程将看到对不可变对象的引用。

这是否意味着尽管单个最终字段(组成一个不可变的对象)没有同步(例如,可见性)问题。但是,不可变对象本身在线程中首次创建时可能在其他线程中不可见(正确创建)吗?

如果是这样,尽管我们可以在线程之间共享初始化的不可变对象而没有任何线程不安全的担心,但是在创建时,它们像其他可变对象一样需要对线程安全的“特殊照顾”?


问题答案:

JLS第17.5节中定义的final字段的语义保证:

保证只有在对象完全初始化之后才能看到对对象的引用的线程才能确保看到该对象的最终字段的正确初始化值。

换句话说,它表示 如果 线程 看到 一个完全初始化的对象, 那么 可以保证看到其最终字段正确初始化。

但是,不能保证该对象对于给定线程是可见的。这是一个不同的问题。

如果您不使用某种同步来发布对象的引用,则另一个线程可能永远无法看到对该对象的引用。

考虑以下代码:

final class A {
  private final int x;
  A(int x) { this.x = x; }
  public getX() { return x; }
}

class Main {
  static volatile A a1 = null;
  static A a2 = null;
  public static void main(String[] args) {
    new Thread(new Runnable() { void run() { try {
      while (a1 == null) Thread.sleep(50);
      System.out.println(a1.getX()); } catch (Throwable t) {}
    }}).start()
    new Thread(new Runnable() { void run() { try {
      while (a2 == null) Thread.sleep(50);
      System.out.println(a2.getX()); } catch (Throwable t) {}
    }}).start()
    a1 = new A(1); a2 = new A(1);
  }
}

请注意,该a1字段是易失的。这确保了最终,对该字段的写入将在一段时间后对所有读取该字段的线程可见。该字段a2不是易失性的(因此,一个线程对该字段的写入可能永远不会被其他线程注意到)。

在此代码中,我们可以确定线程1将完成执行(即,它将看到a1 != null。),但是,线程2可能会暂停,因为它将永远不会看到对该字段的写入a2,因为它不是易失的。



 类似资料:
  • 我已经面临这个问题很多天了,请帮我解决。我正在使用线程同步实现生产者-消费者示例。我在这个传统节目中做了一些调整。我没有只使用一个队列对象,而是使用了两个队列对象。但程序不起作用。。(PS-我知道我可以通过只使用队列的一个对象来让这个程序工作,但如果我想使用两个队列对象呢??) 类队列{ } 类生产者实现Runnable{ } 类消费者实现可运行{ } 公共类测试队列{ }

  • 问题内容: 代码段-1 代码段-2 尽管第二个代码段在不引起任何竞争条件的情况下运行良好,但第一个代码段未能成功同步同一类的不同实例(RequestObject)之间对静态数据成员的访问。有人可以对此进行更多说明。我想了解为什么第一种方法不起作用。 问题答案: 您将不断创建新的对象,然后对其进行同步,这至少使考虑它变得非常混乱。这样就可以得到以下情况: 线程A保持当前值(假设为0) 线程B排队等待

  • 问题内容: 考虑下面的Java示例。请注意,两个类成员变量都没有声明为。如果我正确地理解了内存模型并理解了“之前发生”的规则,那么Java实现可以优化该方法,使其永远运行,即使另一个线程调用该方法也是如此。之所以会发生这种情况,是因为该方法中没有任何东西可以强制线程读取多个值。那是对的吗?如果没有,为什么不呢? 问题答案: 可以由另一个线程修改,但这不是保证。这也不是线程安全的。要使变量保证从另一

  • 问题内容: 代码段-1 代码段-2 我在第一个代码段中遇到了竞争。我知道这是因为我正在获得对不可变对象(类型为Integer)的锁定。 我已经写了第二个代码片段,这再次使“布尔”不变。但这有效(输出运行中不显示竞争条件)。如果我正确理解了上一个问题的解决方案,则以下是出现问题的一种可能方法 线程1锁定由指向的对象(例如A) 线程2现在试图获取由指向的对象的锁,并进入A的等待队列。 线程1进入同步块

  • 问题内容: 我总是理解静态变量在被引用时共享一个实例。我想对此进行测试,但结果与我预期的不同。 输出: 柜台:1 测试:0 由于引用我认为,当我增加那么将自动以及递增。但是,似乎是从某处引用,问题是在哪里? 问题答案: 由于引用 这个假设是错误的。在Java中,您不能引用变量。变量中存储的是一个值。该值可以是原始类型值或引用类型值。对于基元,值是基元的值。对于引用类型, 引用值(通常只是引用)是指

  • 如果我没有错的话,Thread-1和thread-3正在进入synchronized方法,因为它有两个不同的目标对象。但是为什么线程2进入同步块呢? 请帮助我理解这一点。提前谢了。