首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >对可变对象的AtomicReference和可见性

对可变对象的AtomicReference和可见性
EN

Stack Overflow用户
提问于 2012-02-21 13:19:07
回答 4查看 3.3K关注 0票数 9

假设我有一个对象列表的AtomicReference

代码语言:javascript
复制
AtomicReference<List<?>> batch = new AtomicReference<List<Object>>(new ArrayList<Object>());

线程将元素添加到此列表中:batch.get().add(o);

稍后,线程B获取列表,例如,将其存储在DB:insertBatch(batch.get());

我是否必须在写入(线程A)和读取(线程B)时进行额外的同步,以确保线程B以A离开列表的方式查看列表,还是由AtomicReference处理?

换句话说:如果我有一个可变对象的AtomicReference,而一个线程改变了这个对象,其他线程会立即看到这个变化吗?

编辑:

也许一些示例代码是合适的:

代码语言:javascript
复制
public void process(Reader in) throws IOException {
    List<Future<AtomicReference<List<Object>>>> tasks = new ArrayList<Future<AtomicReference<List<Object>>>>();
    ExecutorService exec = Executors.newFixedThreadPool(4);

    for (int i = 0; i < 4; ++i) {
        tasks.add(exec.submit(new Callable<AtomicReference<List<Object>>>() {
            @Override public AtomicReference<List<Object>> call() throws IOException {

                final AtomicReference<List<Object>> batch = new AtomicReference<List<Object>>(new ArrayList<Object>(batchSize));

                Processor.this.parser.parse(in, new Parser.Handler() {
                    @Override public void onNewObject(Object event) {
                            batch.get().add(event);

                            if (batch.get().size() >= batchSize) {
                                dao.insertBatch(batch.getAndSet(new ArrayList<Object>(batchSize)));
                            }
                    }
                });

                return batch;
            }
        }));
    }

    List<Object> remainingBatches = new ArrayList<Object>();

    for (Future<AtomicReference<List<Object>>> task : tasks) {
        try {
            AtomicReference<List<Object>> remainingBatch = task.get();
            remainingBatches.addAll(remainingBatch.get());
        } catch (ExecutionException e) {
            Throwable cause = e.getCause();

            if (cause instanceof IOException) {
                throw (IOException)cause;
            }

            throw (RuntimeException)cause;
        }
    }

    // these haven't been flushed yet by the worker threads
    if (!remainingBatches.isEmpty()) {
        dao.insertBatch(remainingBatches);
    }
}

这里发生的事情是,我创建了四个工作线程来解析一些文本(这是Reader in方法的process()参数)。每个工作人员保存它在批处理中解析的行,并在批处理满时刷新批处理(dao.insertBatch(batch.getAndSet(new ArrayList<Object>(batchSize)));)。

由于文本中的行数不是批处理大小的倍数,所以最后一个对象在没有刷新的批处理中结束,因为它不是满的。因此,这些剩余的批由主线程插入。

我使用AtomicReference.getAndSet()将整个批处理替换为空批。这个程序对线程是正确的吗?

EN

回答 4

Stack Overflow用户

发布于 2012-02-21 13:22:14

嗯..。不是这样的。AtomicReference保证引用本身在线程中是可见的,也就是说,如果您为它分配了与原始引用不同的引用,则更新将是可见的。它不能保证引用所指向的对象的实际内容。

因此,对列表内容的读/写操作需要单独的同步。

编辑:因此,从您更新的代码和您发布的评论来看,将本地引用设置为volatile就足以确保可见性。

票数 11
EN

Stack Overflow用户

发布于 2012-08-14 16:45:54

我想,忘了这里的所有代码,你的问题是:

我是否必须在写入(线程A)和读取(线程B)时进行额外的同步,以确保线程B以A离开列表的方式查看列表,还是由AtomicReference处理?

因此,准确的反应是:,原子负责可见性。这不是我的观点,而是JDK文档1

用于访问和更新原子的内存效果通常遵循挥发物的规则,如Java规范第三版(17.4内存模型)中所述。

我希望这能帮到你。

票数 1
EN

Stack Overflow用户

发布于 2012-02-21 13:33:27

添加到都铎的答案:您将不得不使ArrayList本身线程安全,或者--取决于您的需求--甚至更大的代码块。

如果您可以使用一个线程安全ArrayList,您可以这样“装饰”它:

代码语言:javascript
复制
batch = java.util.Collections.synchronizedList(new ArrayList<Object>());

但是请记住:即使像这样的“简单”结构也不是线程安全:

代码语言:javascript
复制
Object o = batch.get(batch.size()-1);
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9378323

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档