Alexander
Alexander

Reputation: 2898

Java concurrency: safe publication of array

My question is extremely basic: once I have written some array values by one or more threads (phase 1), how can I 'publish' my array to make all the changes visible to other threads (phase 2)?

I have code that does all the array writing, then all the array reading, then again all the writing, then again all the reading etc. I'd like to do it in multiple threads, so multiple threads first would do the array writing phase, then multiple threads would do the array reading phase etc.
My concern is how to safely publish the array writes after each writing phase.

Consider the following simplified thread-unsafe code, that does just one writing phase with just one thread and then just one reading phase with multiple threads:

    ExecutorService executor = Executors.newFixedThreadPool(5);
    double[] arr = new double[5];
    for (int i=0; i<5; ++i) {
        arr[i] = 1 + Math.random();
    }
    for (int i=0; i<5; ++i) {
        final int j=i;
        executor.submit(() -> System.out.println(String.format("arr[%s]=%s", j, arr[j])));
    }

The code normally prints non-zero values, but I understand that it might occasionally print zeros as well, as the array is not properly published by the writing thread, so some writes might not be visible to other threads.

I'd like to fix this problem and write the above code properly, in a thread-safe manner, i.e. to make sure that all my writes will be visible to the reading threads.

1. Could you advise on the best way to do so?
The concurrent collections and AtomicXxxArray are not an option for me because of performance (and also code clarity), as I have 2D arrays etc.

2. I can think of the following possible solutions, but I am not 100% sure they would work. Could you also advise on the solutions below?

Solution 1: assignment to a final array
Justification: I expect a final field to be always properly initialized with the latest writes, including all its recursive dependencies.

    for (int i=0; i<5; ++i) {
        arr[i] = 1 + Math.random();
    }
    final double[] arr2 = arr;  //<---- safe publication?
    for (int i=0; i<5; ++i) {
        final int j=i;
        executor.submit(() -> System.out.println(String.format("arr[%s]=%s", j, arr2[j])));
    }

Solution 2: a latch
Justification: I expect the latch to establish a perfect happens-before relationship between the writing thread(s) and the reading threads.

    CountDownLatch latch = new CountDownLatch(1); //1 = the number of writing threads
    for (int i=0; i<5; ++i) {
        arr[i] = Math.random();
    }
    latch.countDown();    //<- writing is done
    for (int i=0; i<5; ++i) {
        final int j=i;
        executor.submit(() -> {
            try {latch.await();} catch (InterruptedException e) {...} //happens-before(writings, reading) guarantee?
            System.out.println(String.format("arr[%s]=%s", j, arr[j]));
        });
    }

Update: this answer https://stackoverflow.com/a/5173805/1847482 suggests the following solution:

volatile int guard = 0;
...
//after the writing is done:
guard = guard + 1; //write some new value

//just before the reading: read the volatile variable, e.g.
guard = guard + 1; //includes reading
... //do the reading

This solution uses the following rule: "if thread A writes some non-volatile stuff and a volatile variable after that, thread B is guaranteed to see the changes of the volatile stuff as well if it reads the volatile variable first".

Upvotes: 2

Views: 223

Answers (1)

shmosel
shmosel

Reputation: 50746

Your first example is perfectly safe, because the tasks originate from the writer thread. As the docs say:

Actions in a thread prior to the submission of a Runnable to an Executor happen-before its execution begins.

Upvotes: 4

Related Questions