1

我有一个关于通过 Atomic Reference 访问单个元素的问题。如果我有一个 IntegerArray 和一个对它的原子引用;通过 AtomicReference 变量读取和写入数组的各个元素会导致数据竞争吗?

在下面的代码中: num 是一个整数数组,其中 aRnumbers 是对数组的原子引用。在线程 1 和 2 中;我访问 aRnumbers.get()[1] 并将其增加 1。

在两个线程完成后,我可以通过原子引用访问单个元素,而无需每次都使用 22 作为主线程中 aRnumbers.get()[1] 的输出来获得准确的结果。

但是,由于原子引用是在数组上而不是在单个元素上定义的;在这种情况下不应该存在导致 21/22 作为输出的数据竞争吗?

在这种情况下,是否存在数据竞争是拥有 AtomicIntegerArray 数据结构的动机,该数据结构为每个元素提供单独的 AtomicReference ?

请在下面找到我正在尝试运行的 java 代码。任何人都可以让我知道我哪里出错了。

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceExample {


    private static int[] num= new int[2];
    private static AtomicReference<int[]> aRnumbers;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyRun1());
        Thread t2 = new Thread(new MyRun2());

        num[0]=10;
        num[1]=20;

        aRnumbers = new AtomicReference<int[]>(num);

        System.out.println("In Main before:"+aRnumbers.get()[0]+aRnumbers.get()[1]);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("In Main after:"+aRnumbers.get()[0]+aRnumbers.get()[1]);
    }

    static class MyRun1 implements Runnable {
        public void run() {
            System.out.println("In T1 before:"+aRnumbers.get()[1]);
            aRnumbers.get()[1]=aRnumbers.get()[1]+1;

        }
    }

    static class MyRun2 implements Runnable {
        public void run() {
            System.out.println("In T2 before:"+aRnumbers.get()[1]);
            aRnumbers.get()[1]=aRnumbers.get()[1]+1;

        }

    }

}
4

2 回答 2

0

在这种情况下不应该存在导致 21/22 作为输出的数据竞争吗?

确实有。您的线程非常短暂,很可能它们没有同时运行。

在这种情况下,没有数据竞争是拥有 AtomicIntegerArray 数据结构的动机,该数据结构为每个元素提供单独的 AtomicReference 吗?

是的。

任何人都可以让我知道我哪里出错了。

启动一个线程需要 1 - 10 毫秒。

即使没有 JITed 代码,增加这样的值也可能需要 << 50 微秒。如果它经过优化,每次增量大约需要 50 - 200 纳秒。

由于启动线程需要比操作长约 20 - 200 倍的时间,因此它们不会同时运行,因此没有竞争条件。

尝试将值增加几百万次,因此您有一个竞争条件,因为两个线程同时运行。

于 2016-10-30T20:09:54.720 回答
0

增加一个元素包括三个步骤:

  1. 读取值。
  2. 增加值。
  3. 写回值。

可能会出现竞争条件。举个例子:线程 1 读取值(比如说 20)。任务切换。线程 2 读取该值(再次为 20),将其递增并将其写回(21)。任务切换。第一个线程递增该值并将其写回 (21)。因此,虽然发生了 2 次递增操作,但最终值仍仅递增 1。

在这种情况下,数据结构没有帮助。当并发线程添加、访问和删除元素时,线程安全集合有助于保持结构一致。但是在这里,您需要在增量操作的三个步骤期间锁定对元素的访问。

于 2016-10-30T20:15:49.470 回答