gpt4 book ai didi

java - final 字段对于线程安全真的有用吗?

转载 作者:IT老高 更新时间:2023-10-28 20:53:16 26 4
gpt4 key购买 nike

多年来,我每天都在使用 Java 内存模型。我认为我对数据竞争的概念以及避免它们的不同方法(例如,同步块(synchronized block)、 volatile 变量等)有很好的理解。但是,我认为我对内存模型仍有一些不完全了解的地方,即类的最终字段应该是线程安全的,无需任何进一步的同步。

所以根据规范,如果一个对象被正确初始化(也就是说,没有对该对象的引用在其构造函数中以这种引用可以被另一个线程看到的方式转义),那么,在构造之后,任何线程看到对象的人将保证看到对对象的所有最终字段的引用(处于构造时的状态),而无需任何进一步的同步。

特别是,标准(http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4)说:

The usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are.

他们甚至给出了下面的例子:

class FinalFieldExample { 
final int x;
int y;
static FinalFieldExample f;

public FinalFieldExample() {
x = 3;
y = 4;
}

static void writer() {
f = new FinalFieldExample();
}

static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}

线程A应该运行“reader()”,线程B应该运行“writer()”。

显然,到目前为止,一切都很好。

我主要关心的是……这在实践中真的有用吗?据我所知,为了让线程A(正在运行“reader()”)看到对“f”的引用,我们必须使用一些同步机制,比如让f volatile,或者使用锁来同步访问F。如果我们不这样做,我们甚至不能保证“reader()”将能够看到一个初始化的“f”,也就是说,由于我们没有同步访问“f”,读者可能会看到“null"而不是由编写器线程构造的对象。 http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalWrong 中说明了此问题。 ,这是 Java 内存模型的主要引用资料之一[粗体强调我的]:

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. The guarantees the program gets from final fields should be carefully tempered with a deep and careful understanding of how concurrency is managed in your code.

因此,如果我们甚至不能保证看到对“f”的引用,因此我们必须使用典型的同步机制( volatile 、锁等),而这些机制确实已经导致数据竞争消失,那么需要因为 final 是我什至不会考虑的事情。我的意思是,如果为了使“f”对其他线程可见,我们仍然需要使用 volatile 或同步块(synchronized block),并且它们已经使内部字段对其他线程可见......有什么意义(在线程安全方面)首先进入 final ?

最佳答案

我认为您误解了 JLS 示例的意图:

static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}

此代码不保证调用 reader() 的线程会看到 f 的最新值。但它的意思是,如果您确实看到 f 为非空,那么 f.x 保证为 3 ...尽管事实上,我们实际上并没有进行任何显式同步。

那么构造函数中的 final 的隐式同步有用吗?当然是……国际海事组织。这意味着我们每次访问不可变对象(immutable对象)的状态时都需要进行任何额外的同步。这是一件好事,因为同步通常需要缓存读取或写入,这会减慢您的程序速度。

但 Pugh 的意思是,您通常首先需要同步以获取对不可变对象(immutable对象)的引用。他的意思是,使用不可变对象(immutable对象)(使用 final 实现)并不能免除您对同步的需要……或理解应用程序的并发/同步实现的需要。


The problem is that we still need to be sure that reader will se a non-null "f", and that's only possible if we use other synchronization mechanism that will already provide the semantics of allowing us to see 3 for f.x. And if that's the case, why bother using final for thread safety stuff?

同步获取引用和同步使用引用是有区别的。第一个我可能只需要做一次。第二个我可能需要做很多次......使用相同的引用。即使它是一对一的,我仍然将同步操作的数量减半......如果我(假设)将不可变对象(immutable对象)实现为线程安全的。

关于java - final 字段对于线程安全真的有用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22743223/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com