作为一个操作线程访问两个AtomicIntegers是否安全?

时间:2018-09-17 23:13:51

标签: java multithreading thread-safety

在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());
  }
}

我知道上面的代码容易出现竞争状况。

然后他解释如下:

  

像这样的多变量不变量产生了原子性要求:相关变量必须在单个原子操作中获取或更新。您不能更新其中一个,释放并重新获取该锁,然后再更新其他锁,因为这可能涉及在释放锁时使对象处于无效状态。

从这一段中我了解到的是,如果我们使setUppersetLower函数synchronized,那么在某些情况下对象可能达到无效状态。但是,我认为如果两个函数都同步,则只有一个线程可以执行其中一个函数,并且每个函数都必须对不变量进行必要的检查。我们如何才能处于无效状态。任何人都可以举例说明。我在这里想念什么?

如果我正确理解它,那么此行的意义是什么?

  

您不能更新一个,释放并重新获取该锁,然后再更新其他锁,因为这可能涉及在释放锁时使对象处于无效状态。

1 个答案:

答案 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提供了自己的锁定功能,以确保复合动作是原子动作。