2

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".

Malt
  • 28,965
  • 9
  • 65
  • 105
Alexander
  • 2,761
  • 1
  • 28
  • 33
  • And why don't you create your own thread-safe object that manipulates the array? – Evgeni Enchev Jan 24 '19 at 12:13
  • Since I don't want to pay for threads contention. Synchronized access to a large array is too costly for me. – Alexander Jan 24 '19 at 12:20
  • Do all thread have to write to the same array at the same time? Wouldn't it be possible to create a local array in each of the thread and then synchonizely merge all of them into one when all threads are done writing? – Amongalen Jan 24 '19 at 12:24
  • Yes, this might be an option, but writing is not a problem anyway. I can write eventually in one thread, OK. But then I need somehow to safely publish the array(s), so that the reading threads would see the changes. See my code example - it already has only one writing thread. – Alexander Jan 24 '19 at 12:38
  • make a `ReadWriteLock` attach to array,thread write or read array must first get lock. – TongChen Jan 24 '19 at 15:28
  • To the best of my knowledge, ReadWriteLock is quite slow. On other hand, I have to read say an array of 10 million or 100 million entries by say 32 threads. I'd like to avoid the cost of locks when I read the array. So I'd like to just do some action after my writing phase to publish my array changes to the main memory, so all the reading threads could access my array safely. – Alexander Jan 24 '19 at 15:36
  • On other hand, if I just do it once by each thread, before I start the reading, it could be an option... Thank you, it might be a useful answer. Actually, I don't even need a ReadWriteLock, I could just access some synchronized variable. – Alexander Jan 24 '19 at 15:40

1 Answers1

4

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.

shmosel
  • 49,289
  • 6
  • 73
  • 138
  • 1
    Great link, thanks! Actually, your link gives an answer even if I make my example a bit more complex and don't write by the same thread that submits a Runnable: to successfully 'publish' my changes, I just need to use any of the existing synchronization constructs between the array writes and reads, such as CountDownLatch, lock, Phaser, etc. – Alexander Jan 24 '19 at 15:55