Java 原子引用的单个元素上的数据竞争

Java 原子引用的单个元素上的数据竞争,java,arrays,multithreading,atomicreference,Java,Arrays,Multithreading,Atomicreference,我有一个关于通过原子引用访问单个元素的问题。 如果我有一个整数数组和对它的原子引用;通过AtomicReference变量读取和写入数组的各个元素是否会导致数据争用 在下面的代码中:num是一个整数数组,aRnumbers是数组的原子引用。 在螺纹1和2中;我访问aRnumbers.get()[1]并将其递增1 我能够通过原子引用访问单个元素,而无需每次都进行数据竞争,以获得准确的结果,在两个线程完成后,主线程中的aRnumbers.get()[1]输出为22 但是,由于原子引用是在数组上定义的

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

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

我能够通过原子引用访问单个元素,而无需每次都进行数据竞争,以获得准确的结果,在两个线程完成后,主线程中的aRnumbers.get()[1]输出为22

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

在这种情况下,数据竞争不是拥有一个AtomicIntegerArray数据结构的动机吗?它为每个元素提供一个单独的原子引用

请在下面找到我正在尝试运行的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;

        }

    }

}
导入java.util.concurrent.AtomicReference;
公共类原子引用示例{
私有静态int[]num=新int[2];
私有静态原子参考数;
公共静态void main(字符串[]args)引发InterruptedException{
线程t1=新线程(新的MyRun1());
线程t2=新线程(新的MyRun2());
num[0]=10;
num[1]=20;
aRnumbers=新的原子引用(num);
System.out.println(“在Main之前:+aRnumbers.get()[0]+aRnumbers.get()[1]);
t1.start();
t2.start();
t1.join();
t2.连接();
System.out.println(“在Main之后:+aRnumbers.get()[0]+aRnumbers.get()[1]);
}
静态类MyRun1实现Runnable{
公开募捐{
System.out.println(“在T1之前:+aRnumbers.get()[1]);
aRnumbers.get()[1]=aRnumbers.get()[1]+1;
}
}
静态类MyRun2实现Runnable{
公开募捐{
System.out.println(“在T2之前:+aRnumbers.get()[1]);
aRnumbers.get()[1]=aRnumbers.get()[1]+1;
}
}
}
在这种情况下,不应该有数据竞争导致21/22作为输出吗

确实有。您的线程寿命很短,很可能没有同时运行

在这种情况下,数据竞争不是拥有一个AtomicIntegerArray数据结构的动机吗?它为每个元素提供一个单独的原子引用

是的

谁能告诉我哪里出了问题

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;

        }

    }

}
启动线程需要1-10毫秒

即使代码没有被JIT处理,增加这样的值也可能需要花费大量时间 在这种情况下,不应该有数据竞争导致21/22作为输出吗

确实有。您的线程寿命很短,很可能没有同时运行

在这种情况下,数据竞争不是拥有一个AtomicIntegerArray数据结构的动机吗?它为每个元素提供一个单独的原子引用

是的

谁能告诉我哪里出了问题

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;

        }

    }

}
启动线程需要1-10毫秒


即使没有JIT代码,也要增加这样的值,这可能需要增加元素包括三个步骤:

  • 读取值
  • 增加值
  • 将值写回
  • 可能出现竞态条件。举个例子:线程1读取值(假设为20)。任务切换。线程2读取值(再次为20),增加值并将其写回(21)。任务切换。第一个线程递增该值并将其写回(21)。因此,虽然进行了两次递增操作,但最终值仍仅递增一次


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

    增加元素包括三个步骤:

  • 读取值
  • 增加值
  • 将值写回
  • 可能出现竞态条件。举个例子:线程1读取值(假设为20)。任务切换。线程2读取值(再次为20),增加值并将其写回(21)。任务切换。第一个线程递增该值并将其写回(21)。因此,虽然进行了两次递增操作,但最终值仍仅递增一次


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

    是的,根据我的理解,这也是应该发生的。不幸的是(或者更幸运的是)没有,我得到了22作为最终值。我将num[1]初始化为0,并将两个线程中的引用分别递增1000次,现在的预期输出为2000,是的,我确实获得了数据竞赛,但2000从来不是最终输出。对于(int i=0;iAs其他人建议尝试1000000次。对于现代CPU来说这算不了什么。是的,根据我的理解,这也是应该发生的。不幸的是(或者更幸运的是),它没有发生,我得到22作为最终值。我初始化了num[1]在2个线程中,每个线程的引用增加了1000倍,现在预期的输出是2000,是的,我确实得到了数据竞赛,2000从来不是最终输出。对于(int I=0;iAs其他人建议尝试1000000次。这对现代CPU来说不算什么。是的,完美!!我初始化了num[1]为0,并在2 t中分别将参考值增加1000倍