【问题标题】:What does "thread-safe" really mean? [duplicate]“线程安全”的真正含义是什么? [复制]
【发布时间】:2014-03-18 10:07:50
【问题描述】:

来自 Java 并发实践:

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) 无效状态。”

如果AtomicIntegers 是线程安全的,怎么会发生这种情况,我是否遗漏了一些要点?以及如何解决这个问题?

【问题讨论】:

  • 可能是因为原子整数对自己是安全的,但是您正在混合两个独立原子整数的操作。从一个获取,然后在另一个上设置。

标签: java concurrency thread-safety


【解决方案1】:

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

问题来了:

  1. if (i > upper.get)
  2. lower.set(i);

第 1 步和第 2 步可能单独是原子的,但它们一起形成了一个两步的非原子动作。

以下是可能发生的情况:

  1. 如果 blammy 通过
  2. 上下文切换到另一个线程。
  3. 另一个线程调用upper.set(q) 使得q
  4. 上下文切换回此线程。
  5. lower 设置为 i。

每个单独的步骤本质上都是原子的,但步骤的集合不是原子的。

对此的 java 解决方案是:

  1. 同步(some_object_reference,也许这个)
  2. {
  3. if (i > upper.get)
  4. lower.set(i)
  5. }

请确保我们使用相同的对象引用以同步所有设置的上限和下限。

【讨论】:

    【解决方案2】:

    让我们创建一个对象:

    NumberRange nr= new NumberRange();
    

    线程 A:

    nr.setLower(-1); //A1
    

    线程 B:

    nr.setLower(-3); //B1
    nr.setUpper(-2); //B2
    

    执行顺序:B1,然后A1和B2同时:如果线程B在A之前通过检查(-3 set 方法也是,但加在一起你有 2 个原子步骤,而不是一个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-28
      • 1970-01-01
      • 1970-01-01
      • 2012-03-22
      • 1970-01-01
      • 1970-01-01
      • 2017-12-12
      相关资源
      最近更新 更多