同步以确保另一个线程将看到对不可变对象的引用 [英] Synchronize to ensure that reference to immutable object will be seen by another thread

查看:170
本文介绍了同步以确保另一个线程将看到对不可变对象的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I was studying this to understand the behavior of final fields in the new JMM (5 onwards). This concept is clear: guaranteed visibility of initialized final fields to all threads after the object is properly constructed.

但是在本节结尾处,我读到了这个,简单地困惑我:

But then at the end of the section, I read this, which simply confuses me:


现在,如果在线程构造了一个不可变对象(即一个对象之后),只包含final字段),你想确保所有其他线程都能正确看到它,你仍然通常需要使用同步。例如,没有其他方法可以确保第二个线程可以看到对不可变对象的引用。

Now, having said all of this, if, after a thread constructs an immutable object (that is, an object that only contains final fields), you want to ensure that it is seen correctly by all of the other thread, you still typically need to use synchronization. There is no other way to ensure, for example, that the reference to the immutable object will be seen by the second thread.

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

Does this means that though individual final fields (that compose an immutable object) do not have synchronization(say, visibility here) issues. But the immutable object itself when first created in a thread may not be visible (as properly created) in other threads?

如果是这样,尽管我们可以共享初始化的不可变对象线程没有任何线程不安全的担忧,但在创建时,他们需要'特别关注'线程安全,就像其他mutable一样?

If so, though we can share initialized immutable objects across threads without any thread-un-safe worries, but at the time of creation, they need 'special care' for thread safety just like for other mutables?

推荐答案

最终字段的语义,如 JLS的第17.5节,保证:

The semantics of final fields, as defined in section 17.5 of the JLS, guarantee that:


一个只能看到对象引用的线程该对象已完全初始化,保证可以看到该对象的最终字段的正确初始化值。

A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

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

In other words, it says that if a thread sees a completely initialized object, then it is guaranteed to see it's final fields correctly initialized.

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

However, there's no guarantee about the object being visible to a given thread. It is a different problem.

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

If you do not use some kind of synchronization to publish a reference of your object, then the other thread might never be able to see a reference to it.

考虑以下代码:

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 不是易失性的(因此,一个线程对该字段的写入可能永远不会被其他线程注意到)。

Note that the a1 field is volatile. This ensures that, eventually, a write to this field will be made visible to all threads reading it some time later. The field a2 is not volatile (so, a write to this field by one thread might never get noticed by other threads).

在这段代码中,我们可以确定线程1将完成执行(也就是说,它会看到 a1!= null 。但是,它可能会发生线程2将停止,因为它永远不会看到写入字段 a2 ,因为它不是易失性的。

In this code, we can be sure that thread 1 will finish executing (that is, it will see that a1 != null. However, it might happen that thread 2 will halt, as it will never see the write to the field a2, since it is not volatile.

这篇关于同步以确保另一个线程将看到对不可变对象的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆