开发者

In Java, is it required to synchronize write access to an array if each thread writes to a separate cell space?

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.


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.


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.


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)


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.


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


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.


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.


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


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)));
}
}


In general, yes. Try Vector instead.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜