Java - 我应该使用AtomicReferenceArray吗?

时间:2017-11-07 16:59:43

标签: java concurrency parallel-processing

我坚持以下情况:

使用未设置引用(n)的大小为null的数组实例化新对象。

构建步骤应该为每个索引分配一次引用,然后返回完全填充的数组。

复杂性是阵列由多个线程并行提供。

我不关心set操作的原子性,因为两个线程无法访问同一个索引,但是,我想确保返回填充数组的线程“看到”每个填充的索引。 / p>

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

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

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

我应该使用AtomicReferenceArray吗?

非常感谢你的帮助

4 个答案:

答案 0 :(得分:3)

parallelFeed中,启动送纸器线程。假设您的馈线线程不重叠,并且每个线程都有自己的范围,以便它们一起完全填充阵列。然后在parallelFeedjoin线程中。 parallelFeed将等待其他theads完成。假设没有线程无法完成其工作,parallelFeed"看到"所有索引都已填满。

注意

因为你提到" Scala Futures"在你的评论中,看一看  How to wait for several futures有关如何加入"的更多讨论可能发生错误的多个期货。

答案 1 :(得分:1)

如果每个线程只处理它自己的一组索引,那么你需要做的只是{'1}}来自'返回填充数组的线程'的所有线程,以便等到它们全部运行完毕。不,需要join

答案 2 :(得分:1)

如果您只想保证可见性,那么您最常使用的是volatile关键字。不幸的是,volatile does not work well with arrays,你需要在某种程度上解决它。正如该链接所解释的那样,这是可能的,但我不推荐它。

我会将其作为Future的集合来实现。每个任务都是Future<String[]>,“主”线程会将所有结果编译成最终数组。见下文。

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

答案 3 :(得分:0)

一旦填充数组,您就可以使写入线程写入volatile字段。然后在volatile之前阅读return arr字段。由于volatile字段的写入/读取确定了happens-before,因此您将确保数组中数据的可见性。

上面假设必须在线程之间没有额外的同步 - 也就是说,它们不能写入相同的索引,或者像那样写入。

示例代码:

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