Java-我应该使用原子引用阵列吗?
我的处境是: 一个新对象用一个大小为Java-我应该使用原子引用阵列吗?,java,concurrency,parallel-processing,Java,Concurrency,Parallel Processing,我的处境是: 一个新对象用一个大小为n的数组实例化,该数组包含未设置的引用(null) 构建步骤应该为每个索引分配一次引用,然后返回完全填充的数组 其复杂性在于阵列由多个线程并行馈送 我不关心set操作的原子性,因为两个线程不能访问同一个索引,但是,我希望确保返回填充数组的线程“看到”每个填充的索引 private final String[] arr = new String[n]; private void parallelFeed() {...} // Multi-threaded p
n
的数组实例化,该数组包含未设置的引用(null
)
构建步骤应该为每个索引分配一次引用,然后返回完全填充的数组
其复杂性在于阵列由多个线程并行馈送
我不关心set操作的原子性,因为两个线程不能访问同一个索引,但是,我希望确保返回填充数组的线程“看到”每个填充的索引
private final String[] arr = new String[n];
private void parallelFeed() {...} // Multi-threaded
public void build() {
parallelFeed(arr);
return arr;
}
我应该使用原子引用阵列吗
非常感谢您的帮助如果每个线程只处理自己的一组索引,那么您所需要做的就是从“返回填充数组的线程”中加入所有线程,以等待它们全部运行完毕。不需要,需要
AtomicReferenceArray
您可以让写入线程在完成其填充数组的部分后写入volatile
字段。然后读取返回arr
之前的volatile
字段。由于写入/读取volatile
字段会在之前建立,因此可以确保数组中数据的可见性
上面假设线程之间不需要额外的同步——也就是说,它们不能写入相同的索引或类似的smth
示例代码:
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;
}
}
public类writeredsyncarray{
私人最终电子商店;
私有易失布尔同步;
公共写入Adsyncarray(整数大小){
this.store=(E[])新对象[大小];
}
公共无效写入(整数索引,E el){
存储[索引]=el;
sync=true;
}
公共E[]同步数据库(){
boolean localSync=sync;//建立之前发生的所有写入
退货店;
}
}
如果你只想保证可见性,那么你真正想要的就是关键词。不幸的是,不稳定
,您需要稍微解决一下。正如那个链接所解释的,这是可能的,但我不推荐这样做
我将把它实现为一个s的集合。每个任务都是一个未来的
,“主”线程将把所有结果编译成最终的数组。见下文
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();
}
}
import java.util.ArrayList;
导入java.util.array;
导入java.util.List;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.concurrent.Future;
公开课考试
{
私有字符串[]arr=新字符串[2];
public Test()抛出异常//您可能需要更多地处理异常
{//比这更优雅
最终int NUM_线程数=10;
ExecutorService executor=Executors.newFixedThreadPool(NUM_线程);
//开始任务
列表任务=新建ArrayList();
任务。添加(
执行者提交(()->partOne())
);
任务。添加(
executor.submit(()->partTwo())
);
//编译结果
用于(未来任务:任务)
{
最终字符串[]result=task.get();//如有必要,此操作将等待
对于(int i=0;i
在parallelFeed
中,启动进料器线程。假设进料器线程不重叠,并且每个线程都有自己的范围,以便它们一起完全填充阵列。然后在parallelFeed
,join
线程parallelFeed
将等待其他Thead完成。假设没有线程无法完成其任务,parallelFeed
“看到”所有索引都已填充
注意
既然你在评论中提到了“Scala Futures”,请看
更多关于如何“连接”可能发生错误的多个期货的讨论。Volatile.@Michael linked resources没有描述我的建议。那么你的建议是什么?我刚刚意识到OP code暗示的parallelFeed
应该只在所有编写者完成后返回,例如使用join
。如果是这种情况,则无需使用volatile
-join-builds,也可以使用。感谢您的回复,我不确定如何加入
线程。我目前正在使用Scala未来的API,它与Java的API非常相似。我正在等待futures以wait.ready(futures.sequence(futures),1.hour)完成,您知道此操作是否加入线程吗?感谢您的帮助假设futures
只是一个Future
s的列表,是的,这应该会起作用(在修复了输入错误后,futures.sequence
s/bFuture.sequence
)