在Brian Goetz的 Java并发实践中,有一个示例如下:
public class NumberRange {
// INVARIANT: lower <= upper
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(0);
public void setLower(int i) {
// Warning -- unsafe check-then-act
if (i > upper.get())
throw new IllegalArgumentException(
"can't set lower to " + i + " > upper");
lower.set(i);
}
public void setUpper(int i) {
// Warning -- unsafe check-then-act
if (i < lower.get())
throw new IllegalArgumentException(
"can't set upper to " + i + " < lower");
upper.set(i);
}
public boolean isInRange(int i) {
return (i >= lower.get() && i <= upper.get());
}
}
我知道上面的代码容易出现竞争状况。
然后他解释如下:
像这样的多变量不变量产生了原子性要求:相关变量必须在单个原子操作中获取或更新。您不能更新其中一个,释放并重新获取该锁,然后再更新其他锁,因为这可能涉及在释放锁时使对象处于无效状态。
从这一段中我了解到的是,如果我们使setUpper
和setLower
函数synchronized
,那么在某些情况下对象可能达到无效状态。但是,我认为如果两个函数都同步,则只有一个线程可以执行其中一个函数,并且每个函数都必须对不变量进行必要的检查。我们如何才能处于无效状态。任何人都可以举例说明。我在这里想念什么?
如果我正确理解它,那么此行的意义是什么?
您不能更新一个,释放并重新获取该锁,然后再更新其他锁,因为这可能涉及在释放锁时使对象处于无效状态。
答案 0 :(得分:2)
从“ Java并发实践” 本书中
:NumberRange可以通过使用锁定来保持其线程安全性 不变量,例如使用通用锁保护下部和上部。它 还必须避免上下发布,以防止客户 破坏其不变性。
这意味着以下代码是线程安全的:
@ThreadSafe
public class NumberRange {
@GuardedBy("this") private int lower, upper;
public synchronized void setLower(int i) {
if (i > upper) {
throw new IllegalArgumentException("can't set lower to " + i + " > upper");
}
lower = i;
}
public synchronized void setUpper(int i) {
if (i < lower) {
throw new IllegalArgumentException("can't set upper to " + i + " < lower");
}
upper = i;
}
public synchronized boolean isInRange(int i) {
return (i >= lower && i <= upper);
}
}
在这种情况下,NumberRange
提供了自己的锁定功能,以确保复合动作是原子动作。