Collections.synchronizedList(...) 包裹后我可以使用原始列表吗?

Can I use the original list after it has been wrapped by Collections.synchronizedList(...)?

From Javadocs for Collections class:

 public static <T> List<T> synchronizedList(List<T> list)

Returns a synchronized (thread-safe) list backed by the specified list. In order to guarantee serial access, it is critical that all access to the "backing list" is accomplished through the returned list.

我没理解错吗,"backing list" 意思是方法参数(列表),所以在代码行下面 List<String> mySyncList = Collections.synchronizedList(origList); 我永远不会做任何类似 origList.anyMethodCall() 的事情,包括: origList.add("blabla"),还是origList.remove("blabla")origList.set(1, "blabla")?虽然它编译!我能否以不进行结构修改 (origList.contains("blabla")) 的方式访问 "backing list"?我猜,我可以,但也是"access to the backing list"!并且应该遵循官方 Oracle 文档...

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

如果是这样,我是对的吗,如果线程 3 在结构上修改了 origList,那么在任何其他线程中迭代 mySyncList 都会给出 ConcurrentModificationException,但是就线程 3 修改而言绝对没有问题origList非结构化(contains()),或者thread-3在结构上修改了origList但是在mySyncList中没有迭代(mySyncList.add, mySyncList.remove, ...)?

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!
    }

P.S。我的问题不一样,我明白:

It is imperative that the user manually synchronize on the returned list when iterating over it:

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

SO 研究没有得到答案,我仔细检查了以下所有内容:

感谢 GRAY 在下面接受的回答,我终于得出以下结论(我对 Gray 的回答的简要概述):

写完"List mySyncList = Collections.synchronizedList(origList)"建议在下一行写"origList = null"。实际上,guru 永远不会在 code 之后的任何地方使用 origList(在任何线程中,在整个 prog 中)。

理论上origList只要不在结构上修改(add,remove not called)以后使用是没有问题的,但是在实践中谁也不能保证只能非结构访问

其背后的理念是:您将 origList 转换为线程安全的 mySyncList,现在仅将 mySyncList 用于多线程目的而忘记 origList!!!

[After calling synchronizedList(...)] I shall never do anything like origList.anyMethodCall(), including: origList.add("blabla"), or origList.remove("blabla"), origList.set(1, "blabla")?

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

一般来说,一旦您拥有列表的同步包装版本,我会将 origList 设置为 null。没有必要再使用它了。我已经编写并审查了大量的线程代码,从未 看到任何人在包装后使用原始列表。继续使用原始列表真的感觉像是过早的优化和重大的黑客攻击。如果您真的担心性能,那么我会改用 ConcurrentLinkedQueueConcurrentSkipList.

Can I access "backing list" in a way not making structural modifications (origList.contains("blabla"))?

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

Is that right, that problem arises ONLY when origList STRUCTURALLY MODIFIED AFTER I obtained iterator from mySyncList and BEFORE I finished using this iterator?

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

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

It is imperative that the user manually synchronize on the returned list when iterating over it. [removed sample code] Failure to follow this advice may result in non-deterministic behavior.

同步包装器在每次方法调用期间保护底层列表,但如果您使用迭代器遍历您的列表,同时另一个线程正在修改它,则所有赌注都将关闭,因为进行了多个方法调用。见 sample code in the javadocs for more info.

there are absolutely no problems so far as either thread-3 modifies origList non-structurally (contains()), or thread-3 modifies origList structurally but there is no iteration in mySyncList

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

origList.add("blabla"); // prohibited by Oracle ??? :)))

我们不是在谈论 "prohibited",这听起来违反了语言定义。我们正在谈论与正确同步的集合一起工作的正确可重入代码。使用可重入代码,细节决定成败。