首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >安全地使用/交换Collections.synchronizedList

安全地使用/交换Collections.synchronizedList
EN

Stack Overflow用户
提问于 2017-07-11 00:34:48
回答 2查看 257关注 0票数 0

在多线程应用程序中,我们有一个在单例中处理列表的方法。要获取列表的快照,请执行以下操作:

代码语言:javascript
运行
复制
public List swapList() {
    if (syncLinkedList.isEmpty()) {
        return null;
    }
    List currentList = normalLinkList;
    normalLinkList = new LinkedList();//java.util.LinkedList.LinkedList()       
    syncLinkedList = Collections.synchronizedList(normalLinkList);
    return currentList;
}

一个线程在单例之上进行处理。

另一个对象在许多具有套接字的线程上接收请求,在许多线程上向syncLinkedList添加新的请求,如下所示:

代码语言:javascript
运行
复制
syncLinkedList.add(obj);

这是一种安全的交换单例的方式吗?

7月11日更新:项目是否会出现在两个列表之一- currentList或新的normalLinkList?只要项目在其中之一,我们就不会有问题。

EN

回答 2

Stack Overflow用户

发布于 2017-07-11 00:43:47

Short answer NO你的swapList不是踏板安全的。

一些解释:虽然swapList方法会产生线程安全的结果,但它本身并不安全。

解决这个问题的最简单的方法是用synchronized修饰符标记它。

票数 1
EN

Stack Overflow用户

发布于 2017-07-11 15:12:09

这不安全。

当您考虑线程安全时,请始终考虑不同线程上的两个操作,以及它们之间是否存在正式的happens-before relationship。这对于线程安全来说并不总是足够的(取决于您需要完成什么),但它几乎总是必需的。在这种情况下,我们有以下操作:

线程A实例化一个新的列表并将其分配给normalLinkList

  • Thread A将这个新列表包装在一个LinkedList中,然后将其分配给
  1. 大概会修改这个LinkedList;这并不重要,所以让我们暂时将它放在线程A上。稍微分解一下: syncLinkedList
  2. Thread A修改normalLinkList
  3. Thread A释放lock

时,

  • 线程A获得了一个锁

  1. 线程B将normalLinkList读入临时变量currentList,并将其返回
  2. 线程B可能从该列表中读取

这是相当高层次的描述,但它实际上比我们需要的更详细一些。让我们把它归结为真正的本质:

线程A实例化一个新的列表,并将其分配给normalLinkList

  • Thread A。A通过以下方式修改normalLinkList :获取lock

  • modifying

  • LinkedList lock

  • modifying

  • LinkedList normalLinkList

  1. 线程B获取对normalLinkList的引用并通过

对其进行访问

请注意,线程B没有对锁做任何事情。这意味着在它访问列表和线程A的任何修改之间没有发生-之前关系。锁的相关发生-之前关系是:

在监视器上发生解锁-在该监视器上的每个后续锁定之前。

..。但是因为B不会获得锁,所以之前不会发生这种情况。这就是所谓的数据竞赛,这就是为什么JavaDocs for Collections.synchronizedList明确表示:

通过返回的列表完成对支持列表的所有访问,这一点很重要。

那么,你应该怎么做呢?JavaDocs说要做的就是:只访问synchronizedList (决不是它的支持列表),如果你做了任何需要迭代它的事情,只有在持有它的锁的情况下才能这样做。

最简单的方法是根本不交换内容,而是将内容复制到新的列表中,然后清除同步的内容:

代码语言:javascript
运行
复制
// Only do this once. And note, we don't even keep a reference
// to the underlying list. We only access it through syncLinkedList.
syncLinkedList = Collections.synchronizedList(new LinkedList());
...

List readSnapshot;
synchronized (syncLinkedList) {
    readSnapshot = new LinkedList(syncLinkedList); // implicit iteration
    syncLinkedList.clear(); // prepare for next batch (while holding lock)
}
return readSnapshot;

在保持锁的同时调用clear()是很重要的。否则,如果有人在您释放锁之后但在您清除列表之前添加到列表中,则添加的内容将丢失(因为它不在快照中,但无论如何都会被清除)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45017374

复制
相关文章

相似问题

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