数组在 Java 中是线程安全的吗?

2022-09-01 12:11:29

只要索引不同,一个线程从数组的一个索引读取,而另一个线程写入数组的另一个索引,是否存在任何并发问题?

例如(这个例子不一定推荐用于实际使用,只是为了说明我的观点)

 class Test1
 {
     static final private int N = 4096;
     final private int[] x = new int[N];
     final private AtomicInteger nwritten = new AtomicInteger(0);
     // invariant: 
     // all values x[i] where 0 <= i < nwritten.get() are immutable

     // read() is not synchronized since we want it to be fast
     int read(int index) {
         if (index >= nwritten.get())
             throw new IllegalArgumentException();
         return x[index];
     }
     // write() is synchronized to handle multiple writers
     // (using compare-and-set techniques to avoid blocking algorithms
     // is nontrivial)
     synchronized void write(int x_i) {
         int index = nwriting.get();
         if (index >= N)
             throw SomeExceptionThatIndicatesArrayIsFull();
         x[index] = x_i;
         // from this point forward, x[index] is fixed in stone
         nwriting.set(index+1);
     }     
 }

编辑:批评这个例子不是我的问题,我真的只是想知道数组访问一个索引,并发访问另一个索引,会带来并发问题,想不出一个简单的例子。


答案 1

虽然您不会因为更改数组而获得无效状态,但当两个线程在没有同步的情况下查看非易失性整数时,您将遇到相同的问题(请参阅 Java 内存一致性错误教程中的部分)。基本上,问题在于线程 1 可能会在空间 i 中写入一个值,但不能保证线程 2 何时(或是否)会看到更改。

java.util.concurrent.atomic.AtomicIntegerArray 执行你想做的事情。


答案 2

这个例子有很多与散文问题不同的东西。

这个问题的答案是,数组的不同元素是独立访问的,因此,如果两个线程更改了不同的元素,则不需要同步。

但是,Java 内存模型不保证(据我所知)一个线程写入的值对另一个线程可见,除非您同步访问。

根据你真正想要完成的事情,很可能已经有一个班级可以帮你完成。如果没有,我仍然建议查看 的源代码,因为您的代码似乎在做与管理哈希表相同的事情。java.util.concurrentConcurrentHashMap