【发布时间】:2021-10-24 14:57:05
【问题描述】:
我们有一个类 CalcStrategySet,它在并发环境中被多个线程访问。我想知道你们是否可以从下面列出的 2 中建议哪种实现更好。在第一个中,我将变量声明为 volatile 并创建了翻转方法,这是同步的原子操作。其他实现我没有将变量声明为 volatile,但将所有其他方法设为同步。
这些方法是否达到目的并且不会导致竞争条件,如果是,那么哪个更好?
采用易失性和同步原子方法
public class CalcStrategySet {
private volatile CalcStrategy current;
private volatile CalcStrategy backup;
private volatile boolean isBackup;
public CalcStrategySet(CalcStrategy current, CalcStrategy backup) {
this.current = current;
this.backup = backup;
}
public void isStandard() {
return !isBackup;
}
public void merge(CalcStrategy other) {
current.merge(other);
}
public synchronized void flip() {
if(!isBackup) {
current = backup;
backup = null;
isBackup = true;
} else {
throw new IllegalStateException("Already in backup mode");
}
}
}
完全同步
public class CalcStrategySet {
private CalcStrategy current;
private CalcStrategy backup;
private boolean isBackup;
public CalcStrategySet(CalcStrategy current, CalcStrategy backup) {
this.current = current;
this.backup = backup;
}
public synchronized void isStandard() {
return !isBackup;
}
public synchronized void merge(CalcStrategy other) {
current.merge(other);
}
public synchronized void flip() {
if(!isBackup) {
current = backup;
backup = null;
isBackup = true;
} else {
throw new IllegalStateException("Already in backup mode");
}
}
}
【问题讨论】:
-
在您的第一个实现中,没有什么能阻止线程 1 (T1) 在
flip方法中,发现isBackup为假,并且在 T2 进入merge方法并修改 @ 之后的情况987654326@。有时 T2 会在current = backup之前执行此操作,有时在之后执行此操作。对于您的用例,这可能是也可能不是问题。此外,如果CalcStrategy不是线程安全的,则merge方法如果不同步可能会成为问题。 -
@assylias 我会说第一个实现成功地满足了两个(隐式)不变量(1)没有并发翻转(2)在替换为备份之前使用当前策略。如果这是所有 OP 的需要,那么第一个实现就可以了。
标签: java multithreading concurrency synchronized volatile