【问题标题】:Why do I need to synchronize a list returned by Collections.synchronizedList为什么我需要同步 Collections.synchronizedList 返回的列表
【发布时间】:2013-08-02 09:30:48
【问题描述】:

我在 dos.oracle.com 上找到了这个

public static List synchronizedList(List list)

返回由指定支持的同步(线程安全)列表 列表。为了保证串行访问,至关重要的是所有 通过返回的列表完成对后备列表的访问。 用户必须手动同步返回的 迭代时列出:

  List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized(list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

我的问题是:如果Collections.synchronizedList(); 应该返回一个已经同步的列表,为什么我必须同步列表来迭代它?

我只是在两个线程中访问列表:一个线程只是添加,另一个线程获取和删除。对于这种情况,您建议使用哪些其他类?

感谢阅读。

【问题讨论】:

    标签: java multithreading arraylist synchronization


    【解决方案1】:

    正在同步的列表仅意味着addremove 等操作是同步的,因此是原子的。然而迭代不是,如果一个线程 adds 而另一个正在迭代,你可能会得到一个 ConcurrentModificationException。

    通过手动同步您的迭代块,您可以确保列表在迭代时不被修改。

    另一种方法是使用a CopyOnWriteArrayList,它提供an iterator,在迭代开始时迭代列表已知,而不管后续修改如何。但是,如果您需要经常更改列表的内容,那么该集合就不是很有效。

    【讨论】:

    • 谢谢!我会赞成它,但我没有足够的声誉。 CopyOnWriteArrayList 听起来不错,但是是的,这是一个非常频繁的过程,CopyOnWriteArrayList 可能有点慢。
    • +1。你怎么知道addremote等是同步的? javadoc中没有关于它的内容。
    • @LuisSep 您可以得出结论,基于javadoc说列表是同步的并且迭代应该获取列表的监视器(这与正在同步的方法一致)的事实。看一下实现就可以证实这一点。
    • 所以如果我必须添加 synchronized(list){...} 块,我什至需要一个同步列表,还是我可以在这个块内使用普通的 ArrayList?
    • @Tobag 您只需要使用 synchronized 关键字进行迭代 - 添加、删除等“简单”操作是原子的。如果您使用 ArrayList,您也需要同步这些调用。
    【解决方案2】:

    起初我对这个话题有点困惑,因为我发现的大多数示例都没有上下文。我终于找到了这篇为我解决问题的博文:http://netjs.blogspot.de/2015/09/how-and-why-to-synchronize-arraylist-in-java.html

    从上面的示例看来,我只需要使用 Collections.synchronizeList() 转换我的列表,然后我就可以添加和删除项目而无需担心线程安全。但这里需要注意的是,您必须在将列表传递给不同线程之前对其进行同步,否则列表访问不是互斥的。

    所以一个完整的例子是:

    public class SynchroProblem implements Runnable{
      private List<Integer> myList;
    
      //Constructor
      public SynchroProblem(List<Integer> myList){
        this.myList = myList;
      }
    
      @Override
      public void run() {
        // Do stuff with the list .add(), .remove(), ...
        myList.add(5);
    
        // Even if mylist is synchronized the iterator is not, 
        // so for using the iterator we need the synchronized block
        synchronized (myList){
          // do stuff with iterator e.g.
          Iterator<Integer> iterator = myList.iterator();
          while (iterator.hasNext()){
            int number = iterator.next();
            if (number == 123){
              iterator.remove();
            }
          }
    
        }
      }
    
      public static void main(String[] args) {
    
        List<Integer> originalList = new ArrayList<Integer>();
    
        // Synchronize list
        List<Integer> syncList = Collections.synchronizedList(originalList);
    
        // Create threads and pass the synchronized list
        Thread t1 = new Thread(new SynchroProblem(syncList));
        Thread t2 = new Thread(new SynchroProblem(syncList));
    
        t1.start();
        t2.start();
    
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-17
      相关资源
      最近更新 更多