非常基本的问题,但是,如果你把它放在变量之前,那么"最终"会做什么,例如下面...
final EditText myTextField = (EditText) findViewById(R.id.myTextField);
Run Code Online (Sandbox Code Playgroud)
怎么final办?
在阅读" 实践中的Java并发 "和" 实践中的OSGI "后,我发现了一个非常有趣的特定主题; 安全出版.以下内容来自JCIP:
要安全地发布对象,必须同时使对象的引用和对象的状态对其他线程可见.正确构造的对象可以通过以下方式安全发布:
- 从静态初始化程序初始化对象引用.
- 将对它的引用存储到volatile字段中.
- 将对它的引用存储到最终字段中.
- 将对它的引用存储到由(同步)锁定正确保护的字段中.
我的第一个问题:有多少java开发人员知道这个(问题)?有多少真实世界的Java应用程序真正遵循这个,这真的是一个真正的问题吗?我有一种感觉,99%的已实现的JVM不是那种"邪恶",即一个线程不能保证(事实上它实际上(几乎)"不可能")看到陈旧数据只是因为引用不遵循上面的"安全出版成语".
我们有一个共享的ConcurrentHashMap,由2个线程读取和写入.
class Test {
ConcurrentHashMap map;
read() {
map.get(object);
}
write() {
map.put(key, object);
}
}
Run Code Online (Sandbox Code Playgroud)
我们是否需要使地图变得易变,以便读者线程尽快看到一个线程的写入?
是否有可能在一个线程中放置到地图的位置不会被另一个线程看到或看得很晚?HashMap也有同样的问题.
假设我有一个静态复杂对象,它由一个线程池定期更新,并在一个长时间运行的线程中或多或少地连续读取.对象本身总是不可变的,反映了最近的某种状态.
class Foo() { int a, b; }
static Foo theFoo;
void updateFoo(int newA, int newB) {
f = new Foo();
f.a = newA;
f.b = newB;
// HERE
theFoo = f;
}
void readFoo() {
Foo f = theFoo;
// use f...
}
Run Code Online (Sandbox Code Playgroud)
我至少不在乎我的读者是看到旧的还是新的Foo,但是我需要看到一个完全初始化的对象.IIUC,Java规范说没有HERE中的内存屏障,我可能会看到一个fb初始化但尚未提交到内存的对象.我的程序是一个真实世界的程序,它迟早会将内容提交到内存中,所以我不需要立即将新的值ofFoo提交到内存中(虽然它不会受到伤害).
您认为实现内存屏障最可读的方式是什么?如果需要,我愿意为了可读性而付出一点性能价格.我想我可以将赋值同步到Foo,这样可行,但是我不确定读取代码的人为什么这么做是非常明显的.我还可以同步新Foo的整个初始化,但这会引入更多实际需要的锁定.
你会如何编写它以使其尽可能可读?
对Scala版本的奖励荣誉:)
我在一本书中读过这句话,但我不理解:
静态和最终的字段只有一个无法更改的存储空间.
有谁可以帮我解释一下?
这是来自JCiP的示例。
public class Unsafe {
// Unsafe publication
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
}
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.");
}
}
}
Run Code Online (Sandbox Code Playgroud)
在第34页上:
[15]这里的问题不是Holder类本身,而是Holder没有正确发布。但是,可以通过将n字段声明为final来使Holder免受不适当发布的影响,这将使Holder不变。
从这个答案:
final的规范(请参阅@andersoj的答案)保证当构造函数返回时,将对final字段进行适当的初始化(从所有线程可见)。
从维基:
例如,在Java中,如果已经内联了对构造函数的调用,则一旦分配了存储空间,但是在内联的构造函数初始化对象之前,可以立即更新共享变量。
我的问题是:
因为:(可能不正确,我不知道。)
a)可以在内联构造函数初始化对象之前立即更新共享变量。
b)只有在构造函数返回时,才能保证对final字段进行正确的初始化(从所有线程可见)。
另一个线程是否可能看到默认值holder.n?(即,另一个线程holder在holder构造函数返回之前获得对它的引用。)
如果是这样,那么您如何解释以下声明?
通过将n字段声明为final,可以使Holder免受不适当发布的影响,这将使Holder不可变
编辑: …
java ×6
concurrency ×2
final ×1
immutability ×1
modifiers ×1
publish ×1
scala ×1
static ×1
variables ×1