AtomicReference<ArrayList>困难和替代方案

问题描述 投票:0回答:2

所以在我写的各种好玩的程序中,我都遇到过并发修改异常。

在解决这个问题的天真尝试中,我使用了原子而不是某种并发集合。我有点熟悉为什么这会导致错误。本质上,ArrayList 的各个元素不是同步的,可以由不同的线程随意修改。


有人可以帮我总结一下吗
  • 当我尝试对集合进行原子引用时会发生什么错误
  • 对数组或列表的原子引用的合法用例是什么
  • 有哪些更好的替代方法来存储可供多线程使用的游戏实例

  • java concurrency
    2个回答
    4
    投票

    使用

    AtomicReference
    存储对象(例如集合)不足以使其成为线程安全的。事实上,如果您放入
    AtomicReference
    中的对象不像
    ArrayList
    那样是线程安全的,那么如果我们有多个线程尝试同时修改其状态,那么使用它仍然是不安全的。所以好的方法仍然是在你的
    AtomicReference
    中放入一个不可变的对象,这样它的状态就不能再被多个线程修改。例如,如果是集合,您可以使用
    Collections.unmodifiable*
    类型的方法,例如用于列表的
    Collections.unmodifiableList(List)
    ,以便将集合的不可变版本放入
    AtomicReference
    中。

    如果你需要线程安全的集合,你应该看看包中的类

    java.util.concurrent
    ,你会发现集合本身是线程安全的。例如,如果您主要阅读并且很少修改您的
    List
    ,则可以使用线程安全且高效的列表CopyOnWriteArrayList


    2
    投票

    我认为您混淆了 ConcurrentModification 的含义......

    最常见的情况是当您迭代集合并在循环中修改它时。

    例如,如果您执行以下操作

        public static void main(String[] args) {
            List<String> l = new LinkedList<>();
            for(int i=0; i < 100; i++) {
                l.add("banana"+i);
            }
    
            for (String s : l) {
                if("banana10".equals(s)) {
                    l.remove(s);
                }
            }
        }
    

    ...这会给你一个 ConcurrentModificationException。请注意,我还没有生成任何线程。

    正确的做法如下:

    public static void main(String[] args) {
        List<String> l = new LinkedList<>();
        for(int i=0; i < 100; i++) {
            l.add("banana"+i);
        }
    
        for (Iterator<String> iterator = l.iterator(); iterator.hasNext();) {
            String s = iterator.next();
            if("banana10".equals(s)) {
                iterator.remove();
            }
        }
    }
    

    注意在循环遍历集合时使用迭代器来修改集合。

    所以,我不认为你有并发问题!

    如果你想让你的集合线程安全,你需要看看线程安全的语义。如果你想允许多个线程访问同一个集合,ConcurrentList 将是一个很好的方法。如果您想要一个作为整体原子设置的列表引用,您可以使用原子引用。

    © www.soinside.com 2019 - 2024. All rights reserved.