因为volatile只适用于变量引用,所以无论如何都要从一个线程写入(添加)List到另一个线程可见吗?
我的代码有两个线程,一个线程A将很多项推送到List,另一个线程B在线程A完成后读取List; CountDownLatch同步两个线程。当线程B读取列表时,它会发生多次,而不是列表中的所有项目都可见。
我的代码看起来像(实际代码比这更复杂,下面的代码太简单了,无法重现问题):
public class TestList {
@Test
public void test() throws InterruptedException {
int num = 1000;
final CountDownLatch latch = new CountDownLatch(1000);
final List<Integer> list = new ArrayList<Integer>();
for (int i=0; i<num; i++) {
final int finalI = i;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
list.add(finalI);
latch.countDown();
}
});
t.start();
}
latch.await(1, TimeUnit.MINUTES);
System.out.println(list.size());
}
}
更新 感谢所有答案。正如@ MikeStrobel的声明,我终于意识到它不仅是内存可见性问题,而且是同步问题。所以解决方案应该是凯文所说的集合#synchronizedList(...),或者是同步关键字
答案 0 :(得分:2)
答案 1 :(得分:1)
变化:
t.run();
要:
t.start();
实际启动一个帖子。调用run()
方法就像在同一个线程中的普通方法调用一样。
使用2 CountDownLatch
以正确的方式完成。
示例代码:
int num = 1000;
final CountDownLatch startSignal = new CountDownLatch(1); // start from 1
final CountDownLatch doneSignal = new CountDownLatch(1000); // till 1000
final List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < num; i++) {
final int finalI = i;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
startSignal.await(); // wait for start signal
list.add(finalI);
doneSignal.countDown(); // count down the done signal
}
});
t.start();
}
startSignal.countDown(); // let all threads proceed
doneSignal.await(1, TimeUnit.MINUTES); // wait for all to finish
System.out.println(list.size()); // prints 1000
同时查看ExecutorService。
答案 2 :(得分:1)
您未查看CountDownLatch.await()
的结果。因此,如果由于超时而返回(即,如果它返回false),则生产者线程尚未完成其工作,并且您无法安全地访问该列表。
如果使用得当,CountDownLatch类提供可见性保证。