所以我一直在阅读并发问题并且在路上有一些问题(guide I followed - 虽然我不确定它是否是最好的来源):
我会在这里停下来,因为我觉得如果我没有回答这些问题就进一步,我将无法理解后来的课程。
答案 0 :(得分:4)
数目:
答案 1 :(得分:2)
我发现用于解释同步和波动的例子是人为的,难以理解的目的。以下是我的首选示例:
<强>同步强>
private Value value;
public void setValue(Value v) {
value = v;
}
public void doSomething() {
if(value != null) {
doFirstThing();
int val = value.getInt(); // Will throw NullPointerException if another
// thread calls setValue(null);
doSecondThing(val);
}
}
如果在单线程环境中运行,上面的代码是完全正确的。但是,即使是2个线程,也有可能在检查和使用它之间更改value
。这是因为方法doSomething()
不是原子。
要解决此问题,请使用同步:
private Value value;
private Object lock = new Object();
public void setValue(Value v) {
synchronized(lock) {
value = v;
}
}
public void doSomething() {
synchronized(lock) { // Prevents setValue being called by another thread.
if(value != null) {
doFirstThing();
int val = value.getInt(); // Cannot throw NullPointerException.
doSecondThing(val);
}
}
}
<强>易失性:强>
private boolean running = true;
// Called by Thread 1.
public void run() {
while(running) {
doSomething();
}
}
// Called by Thread 2.
public void stop() {
running = false;
}
要解释这一点,需要了解Java内存模型。值得深入阅读,但这个例子的简短版本是Threads有自己的变量副本,这些变量只与同步块上的主内存同步,并且当达到volatile变量时。允许Java编译器(特别是JIT)将代码优化为:
public void run() {
while(true) { // Will never end
doSomething();
}
}
要防止此优化,您可以将变量设置为volatile,这会强制线程在每次读取变量时访问主内存。请注意,如果您使用synchronized语句,则这是不必要的,因为两个关键字都会导致同步到主内存。
我没有像弗朗西斯那样直接解决你的问题。我希望这些示例能够以比您在Oracle教程中看到的示例更好的方式让您了解这些概念。