【发布时间】:2020-04-28 23:17:43
【问题描述】:
考虑以下代码:
public static void main(String[] args) throws InterruptedException {
int nThreads = 10;
MyThread[] threads = new MyThread[nThreads];
AtomicReferenceArray<Object> array = new AtomicReferenceArray<>(nThreads);
for (int i = 0; i < nThreads; i++) {
MyThread thread = new MyThread(array, i);
threads[i] = thread;
thread.start();
}
for (MyThread thread : threads)
thread.join();
for (int i = 0; i < nThreads; i++) {
Object obj_i = array.get(i);
// do something with obj_i...
}
}
private static class MyThread extends Thread {
private final AtomicReferenceArray<Object> pArray;
private final int pIndex;
public MyThread(final AtomicReferenceArray<Object> array, final int index) {
pArray = array;
pIndex = index;
}
@Override
public void run() {
// some entirely local time-consuming computation...
pArray.set(pIndex, /* result of the computation */);
}
}
每个 MyThread 完全在本地计算某些东西(无需与其他线程同步)并将结果写入其特定的数组单元。主线程一直等到所有 MyThreads 都完成,然后检索结果并对其进行处理。
使用AtomicReferenceArray 的get 和set 方法提供了一种内存排序,可保证主线程将看到MyThreads 写入的结果。
但是,由于每个数组单元只被写入一次,并且没有 MyThread 必须看到任何其他 MyThread 写入的结果,我想知道这些强排序保证是否真的必要,或者以下代码是否具有普通数组单元访问,将保证始终产生与上述代码相同的结果:
public static void main(String[] args) throws InterruptedException {
int nThreads = 10;
MyThread[] threads = new MyThread[nThreads];
Object[] array = new Object[nThreads];
for (int i = 0; i < nThreads; i++) {
MyThread thread = new MyThread(array, i);
threads[i] = thread;
thread.start();
}
for (MyThread thread : threads)
thread.join();
for (int i = 0; i < nThreads; i++) {
Object obj_i = array[i];
// do something with obj_i...
}
}
private static class MyThread extends Thread {
private final Object[] pArray;
private final int pIndex;
public MyThread(final Object[] array, final int index) {
pArray = array;
pIndex = index;
}
@Override
public void run() {
// some entirely local time-consuming computation...
pArray[pIndex] = /* result of the computation */;
}
}
一方面,在普通模式访问下,编译器或运行时可能会在主线程的最后循环中优化对array 的读取访问,并将Object obj_i = array[i]; 替换为Object obj_i = null;(隐式初始化数组),因为数组不是从该线程内修改的。另一方面,我在某处读到Thread.join 使加入线程的所有更改对调用线程可见(这将是明智的),因此Object obj_i = array[i]; 应该看到由i-th MyThread 分配的对象引用.
那么,后面的代码会产生与上面相同的结果吗?
【问题讨论】:
-
是否有充分的理由“手动”执行此操作(即扩展
Thread并担心如何收集结果),而不是仅使用Callable<Object>s,以及将结果放入主线程的数组中? -
在这个简单的例子中,没有。但是,可能有更复杂的情况,
Callable不够用。这更多是关于内存模型的问题,以代码作为具体示例。
标签: java arrays multithreading java-memory-model