来自Java Concurrency In Practice:
package net.jcip.examples;
import java.util.concurrent.atomic.*;
/**
* NumberRange
* <p/>
* Number range class that does not sufficiently protect its invariants
*
* @author Brian Goetz and Tim Peierls
*/
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());
}
}
它说“setLower
和setUpper
都是check-then-act序列,但它们没有使用足够的锁定来使它们成为原子。如果数字范围成立(0,10),并且一个线程调用setLower(5)
而另一个线程调用setUpper(4)
,则一些不幸的时间都会通过设置器中的检查,并且将应用这两个修改。结果是该范围现在保持(5,4)无效状态。“
如果AtomicInteger
是线程安全的,我怎么会错过,我是否错过了一些观点?以及如何解决这个问题?
答案 0 :(得分:1)
AtomicInteger
的参与与您的问题的线程安全无关。
问题在于:
步骤1和2可以是单独原子的,但它们一起构成了两步非原子动作。
以下是可能发生的事情:
每个步骤本质上都是原子的,但步骤的集合不是原子的。
对此的java解决方案是:
请务必使用相同的对象引用来同步上限值和下限值的所有设置。
答案 1 :(得分:0)
让我们创建一个对象:
NumberRange nr= new NumberRange();
主题A:
nr.setLower(-1); //A1
主题B:
nr.setLower(-3); //B1
nr.setUpper(-2); //B2
执行顺序:B1,然后A1和B2同时:如果线程B在A(-3 <-2)之前通过检查,然后A在B设置值之前通过其检查(-1&lt; 0),此代码不会抛出任何错误,因为您的方法不是原子的。检查是原子的,也是set
方法,但是你有2个原子步骤,而不是一个。