Java中的List有内存障碍吗?

时间:2014-08-22 19:02:53

标签: java concurrency

因为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(...),或者是同步关键字

3 个答案:

答案 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类提供可见性保证。