【发布时间】:2018-09-17 23:13:50
【问题描述】:
在 Brian Goetz 的Java Concurrency in Practice中有一个例子如下:
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,那么也会出现对象可能达到无效状态的情况。但是,我认为如果两个函数都是同步的,那么只有一个线程可以执行其中一个函数,并且每个函数都对不变量进行必要的检查。我们怎么能处于无效状态。任何人都可以举例说明。我在这里错过了什么?
如果我理解正确,那么这行的意义是什么:
您不能更新一个、释放并重新获取锁,然后再更新其他的,因为这可能涉及在释放锁时使对象处于无效状态。
【问题讨论】:
-
如果它们是同步的,我看不出有任何问题。使用
AtomicInteger毫无意义。 -
无需花费大量时间运行不同的测试,
i的检查和i的设置之间,lower或upper的值可以通过以下方式更改一个不同的线程,使检查无效。重点是,在get和set之间,需要防止任何其他线程修改值的状态 -
@MadProgrammer 我知道这可能会发生。但是作者在我提到的段落中试图解释什么。
-
@JotWaraich 跟我说的差不多。
get保证方法返回时的值,但是,在它和set之间,状态可以改变,你不能保证在调用set和调用get时状态相同.您需要生成一个原子状态,当您可以推断在调用set时,自调用get以来状态没有被修改。如果get和set在同一个锁的上下文中被调用,那么继续应该是安全的 -
我不认为他在处理这个特殊的 sn-p。他的观点是,当您拥有多变量不变量时,单独保护每个字段是不够的。
标签: java multithreading thread-safety