27

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.

rreyes1979
  • 1,855
  • 3
  • 23
  • 34
  • 8
    The question seems fairly specific, though the type of the array may be relevant. If it's an array of objects, clarify whether you mean overwriting the reference (`array[1] = new Foo(bar)`), or mutating the object (`array[1].setBar(bar)`). @sthupahsmaht, your comment is more general. – Matthew Flaschen May 19 '11 at 15:04
  • +1 to Matthew on the specificity of the question: this is a well-defined question. – Charlie Martin May 19 '11 at 15:07
  • possible duplicate of [java array thread-safety](http://stackoverflow.com/questions/1132507/java-array-thread-safety) – Charlie Martin May 19 '11 at 15:16
  • No it is not. In the other question, threads didn't have separate cell space. – rreyes1979 May 19 '11 at 19:08

10 Answers10

22

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.

Brett Kail
  • 33,593
  • 2
  • 85
  • 90
  • Nice, this should imply inefficient operations on arrays on some architectures, among other things – akappa May 19 '11 at 15:12
  • That's not what your reference says: there are certain operations that aren't subject to data races, but that's not all Array operations. – Charlie Martin May 19 '11 at 15:13
  • 2
    @Charlie: "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.". This means that array[1] and array[2] can be considered as two completely independent variables, so I think this answers OP's question – akappa May 19 '11 at 15:16
  • 2
    @akappa I'm not an architecture expert. However, word tearing was recently discussed on the GCC mailing list on a discussion of the C++0x memory model, and this post was encouraging: http://permalink.gmane.org/gmane.comp.gcc.patches/232708 – Brett Kail May 19 '11 at 15:18
  • @bkail: so it seems that the aforementioned set of architectures is actually empty if we limit ourselves to non-ancient processors... good to know :) – akappa May 19 '11 at 15:23
  • 1
    @bkail: I still maintain you cannot safely manipulate `long` or `double` values without synchronization – Tony the Pony May 19 '11 at 15:32
  • @Jen See my response in your answer. This is akin to "If a tree falls in a forest and no one is around to hear it, does it make a sound?". If all threads are operating independently on their own cell, it does not matter if a thread gets pre-empted mid-write so long as the reader guarantees that the write finishes prior to using the cell (e.g., by using .join() on the writing thread). I'll admit that .join() is a form of synchronization. – Brett Kail Jun 07 '11 at 14:48
  • Oracle broke the link to the document. Here is a new link: [http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.6](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.6) – Nayuki Aug 11 '13 at 04:15
4

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.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
2

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)
Tony the Pony
  • 40,327
  • 71
  • 187
  • 281
  • 1
    The first point is true, the second is not. It's true that changing long/double is not _atomic_, but that's irrelevant if the value is only be read by one thread. – Brett Kail May 19 '11 at 15:20
  • @bkail: Are you sure? What if the writing thread writes one 32-bit word of a `long` when it is interrupted? The reading thread would then read a corrupted value, no? – Tony the Pony May 19 '11 at 15:22
  • 1
    If multiple threads are involved with a single cell, then I agree that synchronization is required, but that wasn't the problem statement. – Brett Kail May 19 '11 at 15:59
  • No, bkail is wrong. Jen is right. 64 bit words on 32 bit machines can be written half and then the thread gets interrupted and the data is inconsistent. – Angel O'Sphere May 19 '11 at 16:19
  • 1
    @Angel I do not dispute that the data is inconsistent. I claim it does not matter if no one reads it. In other words, if the original poster is saying, "from the main thread, spawn 10 threads, join them all, and then view the results; from each thread, write to a unique cell", then it simply doesn't matter if a thread gets interrupted "mid-write" because no one will see the intermediate value. – Brett Kail Jun 07 '11 at 02:26
  • Yes, if every thread writes only to specific cells *and* the array is not resized (it is a Java array, not a collection class), then there is no thread synchronization issue at all. – Tony the Pony Jun 07 '11 at 08:15
  • @bkail sorry I don't get it? Why would one program with multiple threads to synchronize them in such an extraordinary way? I would say your answer only confuses people that are asking stuff here, making it very difficult for them to learn anything. Look at the original question ... if that guy stores longs in the array and has reading threads he needs to synchronize writing and reading. – Angel O'Sphere Jun 07 '11 at 16:00
  • I think bkail's point is that because the threads act on different sections of the array, it's as if they are working with separate objects -- i.e., no thread contention. – Tony the Pony Jun 07 '11 at 16:33
  • @Angel A program that wants to read an unknown amount of data, process it in parallel, and aggregate the result at the end. Having each thread operate on its own cell seems useful. If I understand correctly, you are doubtful that the original poster understood the question he actually asked. I assume he did based on the phrasing of the question, so I gave a straightforward answer. You assume he did not and believe we should give him alternatives instead. It's hard to say which approach is better. – Brett Kail Jun 07 '11 at 16:38
  • @bkail and? why do you give this overcomplicated awnser when it clearly don't fit to the question and is only confusing newbies? (The question was not about spawning 10 threads and joining them later, it was about concurrent write access) – Angel O'Sphere Jun 08 '11 at 11:09
  • 1
    @Angel I give a simple answer to a simple question: concurrent write accesses to separate array cells are safe per the JVM spec. I assume based on the phrasing of the question that the original poster is not a newbie. – Brett Kail Jun 08 '11 at 23:19
  • @bkail: Oh, lol. And I assumed on the phrasing of his question *and* your answer that you both where newbies, my fault then. Sorry for the confusion I may have caused. – Angel O'Sphere Jun 09 '11 at 13:05
1

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.

SyntaxT3rr0r
  • 27,745
  • 21
  • 87
  • 120
1

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.

1

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

richs
  • 4,699
  • 10
  • 43
  • 56
0

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.

Ashwin Jayaprakash
  • 2,168
  • 24
  • 29
0

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
Mark
  • 833
  • 1
  • 9
  • 27
0

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)));
}
}
juanmf
  • 2,002
  • 2
  • 26
  • 28
-2

In general, yes. Try Vector instead.

Charlie Martin
  • 110,348
  • 25
  • 193
  • 263
  • 6
    `Vector` is for all practical purposes **deprecated** there are better alternatives in the `java.util.concurrent` package. Any advice to use `Vector` or `Hashtable` is poor advice. –  May 19 '11 at 15:12
  • 3
    @Charlie Martin: 36K rep and suggesting a class from the past? Did the nineties called saying they want their inneficient, lame, classes back or what!? ;) – SyntaxT3rr0r May 19 '11 at 15:13
  • 1
    Snore. Yes, let's use the new complicated classes when a simple old class works fine. And get off my lawn. – Charlie Martin May 19 '11 at 15:18
  • A class that is @deprecated should not be suggested ;D In non threaded environments java.util.ArrayList is significantly faster ... – Angel O'Sphere May 19 '11 at 16:22
  • 1
    I agree. However, since Vector is NOT deprecated (http://download.oracle.com/javase/6/docs/api/java/util/Vector.html and notice that Jarrod says "for all practical purposes", which translates to "but I don't like it") your advice is misplaced. – Charlie Martin May 19 '11 at 18:49
  • "It's not what we don't know that hurts us; it's what we think we know that ain't so."-- Will Rogers – Charlie Martin May 19 '11 at 18:50