“线程安全”究竟意味着什么?

时间:2014-02-18 14:56:15

标签: java concurrency thread-safety

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

它说“setLowersetUpper都是check-then-act序列,但它们没有使用足够的锁定来使它们成为原子。如果数字范围成立(0,10),并且一个线程调用setLower(5)而另一个线程调用setUpper(4),则一些不幸的时间都会通过设置器中的检查,并且将应用这两个修改。结果是该范围现在保持(5,4)无效状态。“

如果AtomicInteger是线程安全的,我怎么会错过,我是否错过了一些观点?以及如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

AtomicInteger的参与与您的问题的线程安全无关。

问题在于:

  1. if(i&gt; upper.get)
  2. lower.set(ⅰ);
  3. 步骤1和2可以是单独原子的,但它们一起构成了两步非原子动作。

    以下是可能发生的事情:

    1. 如果blammy通过
    2. 上下文切换到另一个线程。
    3. 另一个线程调用upper.set(q)使q&lt;我
    4. 上下文切换回此主题。
    5. lower设置为i。
    6. 每个步骤本质上都是原子的,但步骤的集合不是原子的。

      对此的java解决方案是:

      1. synchronized(some_object_reference,也许这个)
      2. {
      3. if(i&gt; upper.get)
      4. lower.set(I)
      5. }
      6. 请务必使用相同的对象引用来同步上限值和下限值的所有设置。

答案 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个原子步骤,而不是一个。