【问题标题】:client side locking required in Collections.synchronizedListCollections.synchronizedList 中需要客户端锁定
【发布时间】:2015-07-27 06:01:02
【问题描述】:

在为 Collections.synchronizedList 编写 putIfAbsent 方法时,需要在访问列表期间提供显式锁定。

下面的代码 sn -p 更详细地解释它:

class ListHelper <E> {
    List<Employee> employeeList = new ArrayList<Employee>();
    employeeList.add(new Employee());
    //.. add more eomployees like this to the list
    public List<E> list = Collections.synchronizedList(employeeList);

    public boolean putIfAbsent(Employee x) {
                   boolean absent = !employeeList.contains(x);
            if (absent)
               employeeList.add(x);
            return absent;
           }
}

请解释为什么当我们有 Collections.synchronizedList 时在 put-if-absent 方法中需要 synchronized(employeeList),它在 ArrayList 对象上有一个锁。

谢谢

【问题讨论】:

  • list 是一个字段吗? list的初始化表达式中的employeeList是什么?
  • employeeList 是具有员工对象值的简单数组列表
  • 你能发布一个完整的例子吗?我觉得缺少一些上下文。

标签: java concurrency synchronized


【解决方案1】:

让我们看看这种方法的实现:

public boolean putIfAbsent(Employee emp, List employeeList) {
    if (!employeeList.contains(emp)) {
        employeeList.add(emp);
    }
}

从某种意义上说,这样的实现是线程安全的,即使多个线程同时调用它,列表的内部结构也不会受到损害,并且列表将保持正常运行。

但这不是线程安全的,因为可能存在竞争条件导致两个线程将相同的元素添加到列表中两次:

T1: check if the employee is contained in the list: no
T2: check if the employee is contained in the list: no
T1: add the employee to the list
T2: add the employee to the list

因此,您的 putIfAbsent() 方法存在错误。这就是需要同步的原因。

【讨论】:

  • 但是 Collections.synchronizedXX 已经返回同步列表,那么还需要额外同步吗
  • 嗯,你看我的回答了吗?它解释了它。列表已同步的事实并不能阻止线程 T2 在 T1 调用 contains() 之后和调用 add() 之前调用 contains()
  • 所以这意味着 Collections.synchronizedList 内部的添加操作是同步的(线程安全的)但包含不是线程安全的
  • 没有。再次阅读我的答案和我的评论。 contains() 是线程安全的。当 T1 执行 contains() 时,没有人可以调用列表的任何方法,因为 contains() 是同步的。当 T1 执行 add() 时,没有人可以调用列表的任何方法,因为 add() 是同步的。但是,在调用 contains() 和调用 add() 之间,T1 没有执行列表中的任何方法。所以任何其他线程都可以执行 contains()。
猜你喜欢
  • 1970-01-01
  • 2015-02-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-02
  • 1970-01-01
  • 2018-09-14
  • 2015-01-10
  • 1970-01-01
相关资源
最近更新 更多