发布后不可变对象的可见性 [英] visibility of immutable object after publication

查看:153
本文介绍了发布后不可变对象的可见性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个不可变对象,它在类中封装并且是全局状态。

I have an immutable object, which is capsulated in class and is global state.

假设我有2个线程获得此状态,执行myMethod(state)用它。并且让我们先说thread1完成。它修改调用GlobalStateCache.updateState(state,newArgs)的全局状态;

Lets say i have 2 threads that get this state, execute myMethod(state) with it. And lets say thread1 finish first. It modify the global state calling GlobalStateCache.updateState(state, newArgs);

GlobalStateCache {
   MyImmutableState state = MyImmutableState.newInstance(null, null);

   public void updateState(State currentState, Args newArgs){
      state = MyImmutableState.newInstance(currentState, newArgs);
   }
}

因此thread1将更新缓存状态,然后thread2执行同样的,它会覆盖状态(不要记住从thread1更新的状态)

So thread1 will update the cached state, then thread2 do the same, and it will override the state (not take in mind the state updated from thread1)

我搜索谷歌,java规范并在实践中阅读java并发但这是显然没有说明。
我的主要问题是,对于已经读取了不可变状态的线程,可变状态对象值是否可见。我认为它不会看到更改后的状态,只有在更新后才会看到它。

I searched google, java specifications and read java concurrency in practice but this is clearly not specified. My main question is will the immutable state object value be visible to a thread which already had read the immutable state. I think it will not see the changed state, only reads after the update will see it.

所以我无法理解何时使用不可变对象?这是否取决于我是否可以在使用我已经看到的最新状态并且不需要更新状态期间进行并发修改?

So i can not understand when to use immutable objects? Is this depends on if i am ok with concurrent modifications during i work with the latest state i have saw and not need to update the state?

推荐答案

发布似乎是一个有点棘手的概念,它在 java并发实践中解释的方式对我来说效果不好(与许多其他多线程相反)本精彩书籍中解释的概念。

Publication seems to be somewhat tricky concept, and the way it's explained in java concurrency in practice didn't work well to me (as opposed to many other multithreading concepts explained in this wonderful book).

以上为记住,让我们首先弄清楚你问题的一些简单部分。

With above in mind, let's first get clear on some simpler parts of your question.


  • 当你说明时我们先说第一个完成 - 你怎么知道的?或者,更确切地说, thread2 如何知道那个?据我所知,只有在线程 join 中显式或非显式的某种同步才能实现这一点(参见JLS - 17.4.5在订单之前发生的事情)。到目前为止您提供的代码没有提供足够的详细信息来判断是否是这种情况

  • when you state lets say thread1 finish first - how would you know that? or, to be more precise, how would thread2 "know" that? as far as I can tell this could be only possible with some sort of synchronization, explicit or not-so-explicit like in thread join (see the JLS - 17.4.5 Happens-before Order). Code you provided so far does not give sufficient details to tell whether this is the case or not

当您声明 thread1将更新缓存状态时 - thread2 如何知道那个?使用您提供的代码片段, thread2 永远不会知道此更新,看起来完全有可能(但不能保证不记得)

when you state that thread1 will update the cached state - how would thread2 "know" that? with the piece of code you provided, it looks entirely possible (but not guaranteed mind you) for thread2 to never know about this update

当你声明 thread2 ...将覆盖状态 覆盖在这里意味着什么? GlobalStateCache 代码示例中没有任何内容可以保证 thread1 会注意到覆盖。更重要的是,所提供的代码没有提出任何会以某种方式强加在关系之前发生 - 来自不同的线程所以甚至可以推测覆盖可能会发生反过来,你看到了吗?

when you state thread2... will override the state what does override mean here? There's nothing in GlobalStateCache code example that could somehow guarantee that thread1 will ever notice this override. Even more, the code provided suggests nothing that would somehow impose happen-before relation of updates from different threads so one can even speculate that override may happen the other way around, you see?

最后但并非最不重要,不可变状态的措辞对我来说听起来很模糊。鉴于这个棘手的话题,我会说危险的模糊。字段 state 是可变的,可以通过调用方法 updateState 来改变它吗?从你的代码中我宁愿得出结论,假设 MyImmutableState 类的实例是不可变的 - 至少这就是名字告诉我的。

the last but not the least, the wording the immutable state sounds rather fuzzy to me. I would say dangerously fuzzy given this tricky subject. The field state is mutable, it can be changed, well, by invoking method updateState right? From your code I would rather conclude that instances of MyImmutableState class are assumed to be immutable - at least that's what name tells me.

如上所述,到目前为止,使用您提供的代码保证可见的是什么?我害怕的并不多......但也许比什么都没有好。我看到它的方式是......

With all above said, what is guaranteed to be visible with the code you provided so far? Not much I'm afraid... but maybe better than nothing at all. The way I see it is...

对于thread1,保证在调用 updateState 之前它会看到 null从thread2更新或正确构造(有效)对象。更新后,保证可以看到从thread1或thread2更新的正确构造的(有效)对象。请注意,在此更新之后,thread1保证不会看到 null 每个JLS 17.4.5我参考上面的内容(... x和y是同一个线程的动作,而x之前是y按程序顺序...

For thread1, it is guaranteed that prior to invoking updateState it will see either null or properly constructed (valid) object updated from thread2. After the update, it is guaranteed to see either of properly constructed (valid) objects updated from thread1 or thread2. Note after this update thread1 is guaranteed not to see null per the very JLS 17.4.5 I refer to above ("...x and y are actions of the same thread and x comes before y in program order...")

对于thread2,保证与上面非常类似。

For thread2, guarantees are pretty similar to above.

基本上,您提供的代码保证的是,两个线程都会看到 null 或正确构造的一个(有效 MyImmutableState 类的实例。

Essentially, all that is guaranteed with the code you provided is that both threads will see either null or one of properly constructed (valid) instances of MyImmutableState class.

乍一看,上面的保证可能看起来微不足道,但是如果你在引号之上略读一页困惑你(不可变对象可以安全使用等......),你会发现一个值得深入钻研的例子 3.5.1。不正确的出版物:当好的对象变坏时

Above guarantees may look insignificant at the first glance, but if you skim one page above the one with quote that confused you ("Immutable objects can be used safely etc..."), you'll find an example worth deeper drilling into in 3.5.1. Improper Publication: When Good Objects Go Bad.

是的,单独的不可变对象不能保证其可见性,但它至少会保证对象赢得' t从内部爆炸,如3.5.1中提供的示例:

Yeah object being immutable alone won't guarantee its visibility but it at least will guarantee that the object won't "explode from inside", like in example provided in 3.5.1:

public class Holder {
  private int n;

  public Holder(int n) { this.n = n; }

  public void assertSanity() {
    if (n != n)
      throw new AssertionError("This statement is false.");
  }
}

Goetz对上述代码的评论从解释问题的真实性开始可变和不可变对象,

Goetz comments for above code begin at explaining issues true for both mutable and immutable objects,

...我们说Holder 未正确发布。不正确发布的对象可能会出现两件事。其他线程可以看到持有者字段的陈旧值,因此即使已将值放在持有者中,也会看到空引用或其他旧值...
...we say the Holder was not properly published. Two things can go wrong with improperly published objects. Other threads could see a stale value for the holder field, and thus see a null reference or other older value even though a value has been placed in holder...

...然后他潜入了如果对象可变

...then he dives into what can happen if object is mutable,

... 会发生什么,但更糟糕的是,其他线程可以看到更新持有人参考的价值,但持有人状态的陈旧价值。为了使事情更不可预测,线程可能会在第一次读取字段时看到陈旧值,然后在下次读取更新的值,这就是assertSanity可以抛出 AssertionError 。
...But far worse, other threads could see an up-todate value for the holder reference, but stale values for the state of the Holder. To make things even less predictable, a thread may see a stale value the first time it reads a field and then a more up-to-date value the next time, which is why assertSanity can throw AssertionError.

上面的AssertionHorror可能听起来有点反直觉但如果考虑下面的情景,所有魔法都会消失(完全合法)每个Java 5内存模型 - 并且有充分的理由btw):

Above "AssertionHorror" may sound counter-intuitive but all the magic goes away if you consider scenario like below (completely legal per Java 5 memory model - and for a good reason btw):


  1. thread1调用 sharedHolderReference = Holder(42) ;

thread1首先用默认值(0)填充 n 字段,然后在构造函数中分配它但是...

thread1 first fills n field with default value (0) then is going to assign it within constructor but...

...但调度程序切换到thread2,

...but scheduler switches to thread2,

sharedHolderReference 变得对thread2可见,因为,为什么不呢?也许优化热点编译器决定它是一个好时机

sharedHolderReference from thread1 becomes visible to thread2 because, say because why not? maybe optimizing hot-spot compiler decided it's a good time for that

thread2读取up-todate sharedHolderReference ,其中字段值仍然存在0 btw

thread2 reads the up-todate sharedHolderReference with field value still being 0 btw

thread2调用 sharedHolderReference.assertSanity()

thread2 invokes sharedHolderReference.assertSanity()

thread2在 assertSanity 中读取 if 语句的左侧值,即0,那么它将读取右侧值但是...

thread2 reads the left side value of if statement within assertSanity which is, well, 0 then it is going to read the right side value but...

...但是调度程序切换回thread1,

...but scheduler switches back to thread1,

thread1通过在字段中设置 n 字段值42

thread1 completes the constructor assignment suspended at step #2 above by setting n field value 42

值42来完成上面步骤#2中暂停的构造函数赋值来自thread1的n 变得对thread2可见,因为,为什么不呢?也许优化热点编译器决定它是一个好时机

value 42 in the field n from thread1 becomes visible to thread2 because, say because why not? maybe optimizing hot-spot compiler decided it's a good time for that

然后,在某个时刻, scheduler 切换回thread2

then, at some moment later, scheduler switches back to thread2

thread2从上面步骤#6暂停的地方开始,即它读取 if 语句的右侧,好吧,42现在

thread2 proceeds from where it was suspended at step #6 above, ie it reads right-hand side of if statement, which is, well, 42 now

oops我们无辜的如果(n!= n)突然变成 if(0 != 42) ......

oops our innocent if (n != n) suddenly turns into if (0 != 42) which...

...自然抛出 AssertionError

据我所知,不可变对象的初始化安全性只是保证不会发生上述情况 - 不再发生。 ..并且不低于

As far as I understand, initialization safety for immutable objects just guarantees that above won't happen - no more... and no less

这篇关于发布后不可变对象的可见性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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