在Java中,如果每个线程写入单独的单元空间,是否需要同步对数组的写入访问?

发布于 2024-11-08 08:17:29 字数 118 浏览 3 评论 0原文

如果每个线程写入单独的单元空间,是否需要同步 Java 中数组的写入访问?

编辑:具体来说,该数组是原始数组或不可变对象数组。前任。 int 数组或 String 数组。

Is it required to synchronize write access to an array in Java if each thread writes to a separate cell space?

EDIT: Specifically, the array is either a primitive Array or an immutable object array. Ex. An int array or a String array.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

无可置疑 2024-11-15 08:27:41

一般来说,是的。尝试使用矢量代替。

In general, yes. Try Vector instead.

蒗幽 2024-11-15 08:27:11

我正要添加一个与此主题相关的问题。我做了一个测试,在访问数组时强制争用。两者都控制协调访问或使用非同步读取-修改-写入操作(如索引)。

当使用索引时——一些计数器> 1(全部应该为1,否则互斥破坏)

public class App {


static int threads = 1000;
static int maxIndex = 1000;

public static void main(String[] args)
{
    try {
        testThreads();
    } catch (InterruptedException ex) {
        Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
    }
    return;

}

public static void testThreads() throws InterruptedException {

    AtomicInteger[] ids = new AtomicInteger[maxIndex];
    for (int i = 0; i < maxIndex; i++) {
        ids[i] = new AtomicInteger(0);  
    }
    Executor exec = Executors.newFixedThreadPool(threads);
    AtomicInteger index = new AtomicInteger(maxIndex);
    final CountDownLatch startGate = new CountDownLatch(1);
    final CountDownLatch endGate = new CountDownLatch(threads);

    for (int i = 0; i < threads; i++) {
        exec.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    startGate.await();
                    try {
                        int i = maxIndex;
                        while (i > 0) {
                            /** 
                             * Interchanging this lines force or avoid collisions. 
                             */
                         // i = --maxIndex;
                            i = index.decrementAndGet();
                            ids[i].incrementAndGet(); 
                        }
                    } catch(Exception ignored) {
                        System.out.println(ignored);
                    } finally {
                        endGate.countDown();
                    }
                } catch (InterruptedException ignored) {
                }
            }
        });
    }
    startGate.countDown();
    endGate.await();
    System.out.println(new ArrayList(Arrays.asList(ids)));
}
}

I was about to add a Question with this topic. I made a test that force contention while accessing an array. Both controlling coordinated access or with a non-synchronized read-modify-write operation like index--.

When using index-- some counters are > 1 (all should be 1, otherwise the mutual exclusion broke)

public class App {


static int threads = 1000;
static int maxIndex = 1000;

public static void main(String[] args)
{
    try {
        testThreads();
    } catch (InterruptedException ex) {
        Logger.getLogger(App.class.getName()).log(Level.SEVERE, null, ex);
    }
    return;

}

public static void testThreads() throws InterruptedException {

    AtomicInteger[] ids = new AtomicInteger[maxIndex];
    for (int i = 0; i < maxIndex; i++) {
        ids[i] = new AtomicInteger(0);  
    }
    Executor exec = Executors.newFixedThreadPool(threads);
    AtomicInteger index = new AtomicInteger(maxIndex);
    final CountDownLatch startGate = new CountDownLatch(1);
    final CountDownLatch endGate = new CountDownLatch(threads);

    for (int i = 0; i < threads; i++) {
        exec.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    startGate.await();
                    try {
                        int i = maxIndex;
                        while (i > 0) {
                            /** 
                             * Interchanging this lines force or avoid collisions. 
                             */
                         // i = --maxIndex;
                            i = index.decrementAndGet();
                            ids[i].incrementAndGet(); 
                        }
                    } catch(Exception ignored) {
                        System.out.println(ignored);
                    } finally {
                        endGate.countDown();
                    }
                } catch (InterruptedException ignored) {
                }
            }
        });
    }
    startGate.countDown();
    endGate.await();
    System.out.println(new ArrayList(Arrays.asList(ids)));
}
}
深海里的那抹蓝 2024-11-15 08:26:24

不要直接写入共享资源。你会让多线程程序的性能比单线程程序的性能更差。当缓存在块中被获取和失效时,缓存中的垃圾效应。如果您想了解更多信息,请看这个演讲。使用 volatile 和 Synchronize 比什么都不使用要好,但仍然很慢。

https://www.youtube.com/watch?v=VCattsfHR4o

Don't write directly to a shared resource. Your gonna make the multi thread program perform worse than its single thread counter part. Trashing effect in Cache as Cache is fetched and invalidated in blocks. Do look at this talk if you want to know more. Use volatile and Synchronize its better than using nothing but still slow.

https://www.youtube.com/watch?v=VCattsfHR4o
花落人断肠 2024-11-15 08:25:30

并发兴趣邮件列表提出了类似的问题 - “原子字节[]操作?”。

大卫·霍姆斯说:

读/写数组的不同区域应该像
访问独立数组。这就是“不撕词”
保证。不管VM是如何实现阵列的
必须确保这适用于基本数组访问。

正如我所说,尚不清楚 arraycopy 的本机版本是否获得
参与其中。

Similar question posed to the Concurrency Interest mailing list - "Atomic byte[] operations?".

David Holmes said:

Reading/writing different regions of the array should act like
accessing independent arrays. This is what "no word-tearing"
guarantees. No matter how the array is implemented by the VM the VM
has to make sure this holds for basic array accesses.

As I said it is less clear if a native version of arraycopy gets
involved.

不必你懂 2024-11-15 08:24:40

您可以执行您所要求的操作,更新每个索引所保存的内容,但不能保证读取索引中数据的其他线程正在看到当前数据。

Java 中有一个名为 易失性 的关键字,它标记实例和类变量,以便 JVM 知道这些值预计会发生变化,并且不会进行任何读取缓存优化,因为读取线程可能会过时数据。由于您无法将数组索引内容标记为易失性,因此其他读取器可能会获取过时的数据。

在 Java 中使用原始数组仅在非常特定的场景中才是好的做法。您的情况可能是其中一种情况,但我无法从问题中看出,因此我建议您查看 java.util.concurrent 特别位于 CopyOnWriteArrayListCopyOnWriteArraySet 如果您不想重复。

由于某种原因,它们现在已成为标准库的一部分,如果您正在进行大量线程和数据共享,您应该尽可能熟悉java.util.concurrent

You can do what you are asking, updating what each index holds, but you are not guaranteed that other threads reading the data in the indexes are seeing current data.

There is a keyword in Java called volatile that marks instance and class variables so that they JVM knows these values are expected to change and to not do any read cache optimization because the reading threads may get stale data. Since you can't mark array index contents volatile other readers can get stale data.

Using raw Arrays in Java is only good practice in very specific scenarios. Yours might be one of those cases, but I can't tell from the question, therefore I suggest you look at java.util.concurrent specifically at CopyOnWriteArrayList and CopyOnWriteArraySet if you don't want duplicates.

These are part of the standard library for a reason now, and if you are doing heavy threading and data sharing you should be as intimately familiar with java.util.concurrent as possible.

念三年u 2024-11-15 08:23:42

相同的线程是否也在读取该值。如果是这样那就没问题了。如果没有,那么您需要担心其他线程是否看到最新的值。这通常是通过关键工作 volatile 或通过原子变量来处理的。

For example if you have an int [] intArray with three elements.

thread 1 updates intArray[0]=<new value>
thread 2 updates intArray[1]=<new value>
thread 3 updates intArray[2]=<new value>

如果这些线程也用于读取值那么你没问题,但如果一个新线程 -->线程 4 尝试读取值,则不能保证它会看到最新的分配。

如果你使用 AtomicIntegerArray、AtomicLongArray 或 AtomicReferenceArray 就可以了

Are the same threads also reading the value. If so then you are ok. If not then you need to worry about whether other threads see the most recent values. This is usually taken care of through the key work volatile or through atomics variables.

For example if you have an int [] intArray with three elements.

thread 1 updates intArray[0]=<new value>
thread 2 updates intArray[1]=<new value>
thread 3 updates intArray[2]=<new value>

if these threads also are used to read the values then you are ok but if a new thread --> thread 4 attempts to read the values then there is no guarantee that it will see the latest assignment.

you'll be ok if you use AtomicIntegerArray, AtomicLongArray, or AtomicReferenceArray

白鸥掠海 2024-11-15 08:22:48

如果您希望其他线程始终能够看到您写入的最后一个值,则通常需要同步。但我可能会在某些情况下,例如,预先计算一个非常昂贵的 long[] (其中每个条目都会占用大量 CPU),并且您会从多个线程执行此操作,知道这些线程中的任何两个都不得写入同一单元。然后,一旦所有线程完成,您将使用同步一次,以确保每个后续读取都会看到正确的值。类似的事情。

请注意,如果您不想自己处理同步问题,您可能需要研究像 AtomicLongArray 这样的类。作为一个额外的好处,像 AtomicLongArray 这样的类可能由不可能在 100% Java 中重新创建的实现支持。

You typically need to synchronize if you want other threads to be able to always see the last value you've written. But I could some cases where you'd be, say, precomputing a giganticly expensive long[] (where each entry would take a lot of CPU) and you'd be doing that from several threads, knowing no two of these threads shall write to the same cell. Then once all your threads would be done you'd be using synchronization once to be sure each subsequent read would see the correct values. Something like that.

Note that if you don't want to deal yourself with synchronization issues you may want to look into classes like AtomicLongArray. As an added benefit, classes like AtomicLongArray may be backed by an implementation that is impossible to re-create in 100% Java.

青萝楚歌 2024-11-15 08:22:01

这不是一个简单的“是或否”问题。需要考虑的一些事情:

  • 您的问题意味着您正在数组中存储原始类型(或引用)。对存储在数组中的对象执行复杂的操作可能需要同步。
  • 在没有同步的情况下更改 longdouble 值是不安全
  • ,即使您存储的基元 doublelong,由于缓存,其他线程可能无法立即看到更改的值(导致读取过时

Not a simple yes or no issue. Some things to consider:

  • Your question implies that you are storing primitive types (or references) in your array. Performing complex operations on objects stored in the array may require synchronization.
  • Changing long or double values is not safe without synchronization
  • Even if you are storing primitives that are not double or long, there is the possibility of another threads not being able to see changed values immediately because of caching (resulting in stale reads)
那些过往 2024-11-15 08:21:09

如果读访问也以相同的方式分区,则不需要根据 bkail 的链接进行同步。

但是,如果线程读取彼此的写入,则仍然需要有内存屏障强制缓存内容同步。否则,threadys 可能会得到不一致的读取结果。

If read access is also partitioned in the same manner, then synchronization is not needed as per bkail's link.

But if the threads read each other's writes, it would still be necessary to have a memory barrier to force synchronization of cache contents. Otherwise, threadys may get inconsistent read results.

蓝礼 2024-11-15 08:20:19

不,不需要同步。

它在 JLS §17.6 单词撕裂中定义:

Java 虚拟机的一个实现考虑因素是每个字段和数组元素都被认为是不同的;对一个字段或元素的更新不得与任何其他字段或元素的读取或更新交互。特别是,分别更新字节数组相邻元素的两个线程不得干扰或交互,并且不需要同步来确保顺序一致性。

No, synchronization is not needed.

It is defined in JLS §17.6 Word Tearing:

One implementation consideration for Java virtual machines is that every field and array element is considered distinct; updates to one field or element must not interact with reads or updates of any other field or element. In particular, two threads that update adjacent elements of a byte array separately must not interfere or interact and do not need synchronization to ensure sequential consistency.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文