2

I am stuck with the situation where :

A new object is instantiated with an array of size n of unset references (null).

A build step should assign each index a reference exactly once and then returns the full-filled array.

The complexity is that the array is feeded in parallel by multiple threads.

I don't care with the atomicity of the set operation because two threads can't access the same index, however, I would like to be sure that the thread that returns the filled array "sees" every index filled.

private final String[] arr = new String[n];

private void parallelFeed() {...} // Multi-threaded

public void build() {
    parallelFeed(arr);
    return arr;
}

Should I use an AtomicReferenceArray ?

Thanks a lot for your help

ogen
  • 802
  • 2
  • 7
  • 23

4 Answers4

3

In parallelFeed, start your feeder threads. Your feeder threads are assumed to not overlap, and to have each their own range so that together they fully fill the array. Then in parallelFeed, join the threads. parallelFeed will wait for the other theads to complete. Assuming no thread fails to do its job, parallelFeed "sees" that all indexes have been filled.

NOTE

Since you mention "Scala Futures" in your comment, take a look at How to wait for several futures for more discussion on ways to "join" up multiple futures where errors might occur.

Les
  • 10,335
  • 4
  • 40
  • 60
  • Thanks for your reply, not sure of how I should `join` threads. I am currently using the Scala's Future API which is quite similar to Java's one. I am waiting for futures to complete with a `Await.ready(Futures.sequence(futures), 1.hour)`, do you know if this operation joins the threads ? Thanks for your help – ogen Nov 08 '17 at 09:21
  • Assuming `futures` is just a list of `Future`s, yes, that should work (after fixing the typo, `Futures.sequence` s/b `Future.sequence` ) – Les Nov 08 '17 at 11:30
1

If each thread only handles its own set of indexes then all you need to do is join on all the threads from the 'thread that returns the filled array' in order to wait until they all are done running. No, need for AtomicReferenceArray.

tsolakp
  • 5,858
  • 1
  • 22
  • 28
1

If you only want to guarantee visibility, what you're essentially after is the volatile keyword. Unfortunately, volatile does not work well with arrays, and you'd need to work around it somewhat. It's possible, as that link explains, but I wouldn't recommend it.

I would implement this as a collection of Futures. Each task would be a Future<String[]> and the "main" thread would compile all the results into the final array. See below.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test
{
    private String[] arr = new String[2];

    public Test() throws Exception // You may want to handle the exceptions more
    {                              // gracefully than this
        final int NUM_THREADS = 10;
        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);

        // Start the tasks
        List<Future<String[]>> tasks = new ArrayList<>();

        tasks.add(
            executor.submit(() -> partOne())
        );
        tasks.add(
            executor.submit(() -> partTwo())
        );

        // Compile result
        for (Future<String[]> task : tasks)
        {
            final String[] result = task.get(); // This will wait if necessary
            for (int i = 0; i < result.length; ++i)
            {
                if (result[i] != null) arr[i] = result[i];
            }
        }

        System.out.println(Arrays.toString(arr));
        executor.shutdown();
    }

    private static String[] partOne()
    {
        String[] arr = new String[2];
        arr[0] = "Hello";
        return arr;
    }

    private static String[] partTwo()
    {
        String[] arr = new String[2];
        arr[1] = "World";
        return arr;
    }

    public static void main(String... args) throws Exception
    {
        new Test();
    }
}
Michael
  • 41,989
  • 11
  • 82
  • 128
0

You can make writing threads write to a volatile field once they done their part of filling an array. Then read that volatile field before right before return arr. Since writing / reading of volatile field establishes happens-before, you'll ensure visibility of data in array.

Above assumes no additional sync between threads necessary -- i.e., they can't write to same index, or smth like that.

Sample code:

public class WriteReadSyncArray<E> {

    private final E[] store;
    private volatile boolean sync;

    public WriteReadSyncArray(int size) {
        this.store = (E[]) new Object[size];
    }

    public void write(int index, E el) {
        store[index] = el;
        sync = true;
    }

    public E[] syncAndReadArray() {
        boolean localSync = sync; // establishes happens-before with all previous writes
        return store;
    }
}
Victor Sorokin
  • 11,878
  • 2
  • 35
  • 51