有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java将两个原子整数作为一个线程安全的操作来访问?

在Brian Goetz的《实践中的Java并发》(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());
  }
}

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

然后他解释如下:

Multivariable invariants like this one create atomicity requirements: related variables must be fetched or updated in a single atomic operation. You cannot update one, release and reacquire the lock, and then update the others, since this could involve leaving the object in an invalid state when the lock was released.

我从这一段中了解到,如果我们使setUppersetLower函数synchronized,那么也会出现对象可能达到无效状态的情况。然而,我认为如果两个函数都是同步的,那么只有一个线程可以执行其中一个函数,并且每个函数都有必要的不变量检查。我们怎么能处于无效状态。任何人都可以举个例子来演示。我错过了什么

如果我理解正确,那么这句话的意义是什么:

You cannot update one, release and reacquire the lock, and then update the others, since this could involve leaving the object in an invalid state when the lock was released.


共 (1) 个答案

  1. # 1 楼答案

    从《Java并发实践》一书中:

    NumberRange could be made thread-safe by using locking to maintain its invariants, such as guarding lower and upper with a common lock. It must also avoid publishing lower and upper to prevent clients from subverting its invariants.

    这意味着以下代码是线程安全的:

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