Java 在原始列表被Collections.synchronizedList(…)包装后,我可以使用它吗?

Java 在原始列表被Collections.synchronizedList(…)包装后,我可以使用它吗?,java,multithreading,list,collections,synchronized,Java,Multithreading,List,Collections,Synchronized,另外,我的问题不同,我确实理解: 用户必须手动同步返回的数据 在对其进行迭代时列出: 所以研究没有给出答案,我认真地回顾了以下所有内容: 多亏格雷接受了下面的答案,我最终得出了以下结论(我对格雷答案的简要概述): 在写入“List mySyncList=Collections.synchronizedList(origList)”之后,建议在下一行中写入“origList=null”。实际上,guru以后在代码中的任何地方都不会使用origList(在任何线程中,在整个程序中) 理

另外,我的问题不同,我确实理解:

用户必须手动同步返回的数据 在对其进行迭代时列出:

所以研究没有给出答案,我认真地回顾了以下所有内容:

多亏格雷接受了下面的答案,我最终得出了以下结论(我对格雷答案的简要概述):

在写入“List mySyncList=Collections.synchronizedList(origList)”之后,建议在下一行中写入“origList=null”。实际上,guru以后在代码中的任何地方都不会使用origList(在任何线程中,在整个程序中)

理论上,如果origList未在结构上进行修改(添加、删除未调用),则以后使用origList不会有任何问题,但在实践中,没有人能够安全地保证仅使用非结构访问。

这背后的思想是:您将origList转换为线程安全的mySyncList,现在只将mySyncList用于多线程目的,而忘记origList

[调用synchronizedList(…)后]我永远不会做类似origList.anyMethodCall()的事情,包括:origList.add(“blablabla”)或origList.remove(“blablabla”)、origList.set(1,“blablabla”)

没错。每当有多个线程更新列表时,必须确保所有线程都以同步方式处理它。如果一个线程通过
Collections.synchronizedList(…)
包装器访问此列表,而另一个线程不是,则很容易出现数据损坏问题,导致无限循环或随机运行时异常

通常,一旦您获得了列表的同步包装版本,我会将
origList
设置为
null
。不再使用它了。我已经编写并审阅了大量线程化代码,从未见过有人在原始列表包装后使用它。这真的感觉像是过早的优化和一个主要的黑客继续使用原始列表。如果您真的担心性能,那么我会转而使用
ConcurrentLinkedQueue
ConcurrentSkipList

我能否以不进行结构修改的方式访问“支持列表”(origList.contains(“blablabla”))

是的,但是没有线程可以进行结构修改。如果使用同步版本的线程添加了一个条目,然后另一个线程访问非同步版本,那么相同的争用条件可能会导致列表的部分同步版本,从而导致问题

是这样吗?只有在我从mySyncList获得迭代器之后,在我使用完迭代器之前,origList在结构上被修改时,问题才会出现

是的,只要你能保证这一点就可以了。但同样,这感觉像一个黑客。如果有人更改了另一个线程的行为,或者如果将来更改了计时,您的代码将在没有任何警告的情况下开始中断

对于其他人来说,与完全并发的集合(如
ConcurrentSkipList
)相反,同步列表包装器的问题在于迭代器是多个操作。引自:

用户在遍历返回的列表时,必须手动同步该列表。[删除的示例代码]未能遵循此建议可能会导致不确定性行为

同步包装器在每次方法调用期间保护基础列表,但如果您使用迭代器遍历列表,同时另一个线程正在修改列表,则由于进行了多个方法调用,因此所有赌注都被取消。看

就线程3在非结构上修改origList(contains())或线程3在结构上修改origList而言,绝对没有问题,但mySyncList中没有迭代

只要两个线程都在使用同步包装器,那么就应该没有问题


我们不是在说“禁止”,这听起来像是违反了语言定义。我们讨论的是使用正确同步的集合的正确可重入代码。有了可重入代码,魔鬼就在细节中。

确实抓住了这一点。你在问什么?我能否以不进行结构修改的方式访问“支持列表”?只有在安全出版的情况下。
 public static <T> List<T> synchronizedList(List<T> list)
public static void main(String[] args) {
        List<String> origList = new ArrayList<>();
        origList.add("one");
        origList.add("two");
        origList.add("three");

        List<String> mySyncList = Collections.synchronizedList(origList);
        origList.add("blabla"); // prohibited by Oracle ??? :)))
        origList.add("blabla"); // prohibited by Oracle ??? :)))
        origList.add("blabla"); // prohibited by Oracle ??? :)))
        origList.add("blabla"); // prohibited by Oracle ??? :)))
        // now use mySyncList
        System.out.println(mySyncList); // no problem so far

        // P.S.: Maybe problem arises ONLY when origList STRUCTURALLY MODIFIED
        // AFTER I obtained iterator from mySyncList and BEFORE I finished
        // using this iterator? If so, such wording would be much preferable in
        // official docs!
    }
List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }
origList.add("blabla"); // prohibited by Oracle ??? :)))