基本上以下工作但是因为我读到了关于final关键字,如果不同的线程访问它我是否必须声明name final,我不知道了吗?
提前致谢。
public class Test4 {
// to ensure thread-safety do we have to declare the variable name final ?
private String name;
public Test4 (String name) {
this.name = name;
}
public void start() {
new MyThread().start();
}
private class MyThread extends Thread {
public void run() {
System.out.println(name);
}
}
public static void main(String[] args) {
Test4 t = new Test4("Don't know if I am threadsafe");
t.start();
}
}
答案 0 :(得分:4)
final
修饰符 - 同时阻止重新分配成员 - 不影响给定代码 1的正确性
来自Java 5语言规范的17.4.4 Synchronization Order部分:
同步顺序是执行的所有同步操作的总顺序。同步操作会引发与操作同步的关系,定义如下:
- ..
- 启动线程的操作与启动的线程中的第一个操作同步。
- ..
然后,由于设置 name
成员的线程是启动线程的线程,因此保证了同步顺序。 (同步 - 表示Happens-before ordering。)
请注意:
name
只需要在启动线程之前设置:也就是说,不需要在构造函数中设置此同步保证。但是,final
字段确实给人一种更舒适的感觉(参考17.5 Final Field Semantics):
当构造函数完成时,对象被认为是完全初始化的。在该对象完全初始化之后只能看到对象*的引用的线程保证看到该对象的最终字段的正确初始化值。
在这种情况下,使用final字段,保证在构造函数完成后在每个线程上显示该值。 ("constructor leaks"可以违反此保证。)
1 在提供的代码中,“非最终”name
成员仅在线程启动之前分配一次。
在不同的中,可能会暴露其他同步问题。此答案检查是否删除final
是否会改变所提供代码的正确性。
所有这些,我认为使用不可变变量(final
)和不可变对象是“好习惯” - 尤其是在处理线程时。不需要知道JVM的小奥秘细节,而是做出安全可靠的事情,并且在聪明或“表现”方面力求明显的正确性。
另见:
答案 1 :(得分:3)
最终变量是immutable
,一旦构造它就不能更改它,因此它没有并发问题。
答案 2 :(得分:2)
答案 3 :(得分:1)
您是否正在寻找AtomicReference
或volatile
?这取决于你的意思线程安全?
// Atomic to allow deeper control of updates.
private AtomicReference<String> name = new AtomicReference<String>();
// Volatile to ensure it is not cached.
private volatile String vName;
public Test(String name) {
this.name.set(name);
this.vName = name;
}
public void start() {
new MyThread().start();
}
private class MyThread extends Thread {
public void run() {
System.out.println(name.get());
System.out.println(vName);
}
}
答案 4 :(得分:0)
final
与多线程没有任何关系,但是如果你的fild不应该更改并且在类的构造函数中初始化,你应该放final
。这意味着后者不能改变fild。
答案 5 :(得分:0)
因为String是不可变的,你声明字段final,所有线程在字段赋值后访问它,然后肯定不会有并发问题,因为该字段仅用于Read操作,与StringBuilder的情况相反被使用了。