AtomicReference 的单个元素上的数据竞争
Data Races on individual elements of an AtomicReference
我有一个关于通过原子引用访问单个元素的问题。
如果我有一个 IntegerArray 和对它的原子引用;通过 AtomicReference 变量读取和写入数组的各个元素会导致数据竞争吗?
在下面的代码中:num 是一个整数数组,其中 aRnumbers 是对该数组的原子引用。
在线程 1 和 2 中;我访问 aRnumbers.get()[1] 并将其递增 1.
我能够通过原子引用访问单个元素,而无需数据竞争,每次都获得准确的结果,在两个线程完成后,主线程中 aRnumbers.get()[1] 的输出为 22。
但是,由于原子引用是在数组而不是单个元素上定义的;在这种情况下,不应该存在导致输出为 21/22 的数据竞争吗?
在这种情况下,数据竞争难道不是拥有 AtomicIntegerArray 数据结构的动机,该数据结构为每个元素提供单独的 AtomicReference?
请在 java 代码下方找到我正在尝试 run.Could 任何人请告诉我哪里出错了。
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;
}
}
}
shouldn't there be a data race in this case leading to 21/22 as the output?
确实有。您的线程存在时间如此短,很可能它们不会同时 运行。
Isn't having data races in this case the motivation for having a AtomicIntegerArray data structure which provides a separate AtomicReference to each element?
是的,是的。
Could anyone kindly let me know where I am going wrong.
启动线程需要 1 - 10 毫秒。
即使没有对代码进行 JIT,增加这样的值也可能需要 << 50 微秒。如果对其进行优化,则每次增量大约需要 50 - 200 纳秒。
由于启动一个线程所花的时间比运行它们的时间长大约 20 - 200 倍,因此它们不会同时 运行,因此不存在竞争条件。
尝试将该值递增几百万次,因为两个线程同时处于 运行 状态,所以您会遇到竞争条件。
增加一个元素包括三个步骤:
- 正在读取值。
- 增加值。
- 正在写回值。
可能会出现竞争条件。举个例子:线程 1 读取值(比方说 20)。任务切换。线程 2 读取值(再次为 20),将其递增并写回(21)。任务切换。第一个线程递增该值并将其写回 (21)。因此,虽然发生了 2 次递增操作,但最终值仍然只递增了 1。
数据结构在这种情况下没有帮助。线程安全集合有助于在并发线程添加、访问和删除元素时保持结构一致。但是这里需要在自增操作的三个步骤中锁定对某个元素的访问。
我有一个关于通过原子引用访问单个元素的问题。 如果我有一个 IntegerArray 和对它的原子引用;通过 AtomicReference 变量读取和写入数组的各个元素会导致数据竞争吗?
在下面的代码中:num 是一个整数数组,其中 aRnumbers 是对该数组的原子引用。 在线程 1 和 2 中;我访问 aRnumbers.get()[1] 并将其递增 1.
我能够通过原子引用访问单个元素,而无需数据竞争,每次都获得准确的结果,在两个线程完成后,主线程中 aRnumbers.get()[1] 的输出为 22。
但是,由于原子引用是在数组而不是单个元素上定义的;在这种情况下,不应该存在导致输出为 21/22 的数据竞争吗?
在这种情况下,数据竞争难道不是拥有 AtomicIntegerArray 数据结构的动机,该数据结构为每个元素提供单独的 AtomicReference?
请在 java 代码下方找到我正在尝试 run.Could 任何人请告诉我哪里出错了。
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;
}
}
}
shouldn't there be a data race in this case leading to 21/22 as the output?
确实有。您的线程存在时间如此短,很可能它们不会同时 运行。
Isn't having data races in this case the motivation for having a AtomicIntegerArray data structure which provides a separate AtomicReference to each element?
是的,是的。
Could anyone kindly let me know where I am going wrong.
启动线程需要 1 - 10 毫秒。
即使没有对代码进行 JIT,增加这样的值也可能需要 << 50 微秒。如果对其进行优化,则每次增量大约需要 50 - 200 纳秒。
由于启动一个线程所花的时间比运行它们的时间长大约 20 - 200 倍,因此它们不会同时 运行,因此不存在竞争条件。
尝试将该值递增几百万次,因为两个线程同时处于 运行 状态,所以您会遇到竞争条件。
增加一个元素包括三个步骤:
- 正在读取值。
- 增加值。
- 正在写回值。
可能会出现竞争条件。举个例子:线程 1 读取值(比方说 20)。任务切换。线程 2 读取值(再次为 20),将其递增并写回(21)。任务切换。第一个线程递增该值并将其写回 (21)。因此,虽然发生了 2 次递增操作,但最终值仍然只递增了 1。
数据结构在这种情况下没有帮助。线程安全集合有助于在并发线程添加、访问和删除元素时保持结构一致。但是这里需要在自增操作的三个步骤中锁定对某个元素的访问。