以下代码不是线程安全的吗?

时间:2013-09-29 00:29:04

标签: java multithreading thread-safety

以下代码有什么问题?。

private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();    
Record rec = records.get(id);
  if (rec == null) {
      rec = new Record(id);
      records.put(id, rec);
  }
  return rec;
  1. 以上代码不是线程安全吗?在这种情况下,我为什么要使用putIfAbsent
  2. 锁定仅适用于更新。如果是检索,它 允许完全并发。这句话是什么意思?。

2 个答案:

答案 0 :(得分:6)

这不是线程安全的。

  1. 如果还有另一个帖子,那么在records.getrecords.put之间的时间内,另一个帖子也可能放置了该记录。

  2. 只读操作(即不修改结构的操作)可以由多个线程同时完成。例如,1000个线程可以安全地读取int的值。但是,如果没有某种锁定操作,那1000个线程无法更新int的值。

  3. 我知道这可能听起来像是一个非常不可能发生的事件,但请记住,百万分之一的事件在1GHz时每秒发生1000次。


    这是线程安全的:

    private Map<Integer, Integer> aMap = new ConcurrentHashMap<Integer, Integer>();
    // presumably aMap is a member and the code below is in a function
    aMap.putIfAbsent(id, new Record(id))
    Record rec = records.get(id);
    return rec;
    

    请注意,这可能会创建Record并且永远不会使用它。

答案 1 :(得分:3)

它可能是也可能不是线程安全的,具体取决于您希望它如何行动。

在代码结束时,aMapRecord安全地id。但是,两个线程可能创建并放入Record,这样就存在两个(或更多,如果有更多线程的话)Records。这可能没问题,也可能不是 - 真的取决于你的申请。

线程安全的危险之一(例如,如果你使用普通的HashMap没有同步)是线程可以跨线程读取部分创建或部分更新的对象;换句话说,事情可能真的乱七八糟。这将 not 在您的代码中发生,因为ConcurrentHashMap将确保线程之间的内存保持最新,并且在这种意义上它是线程安全的。

你可以做的一件事就是使用putIfAbsent,它会自动将一个键值对放入地图中,但前提是该键上没有任何内容:

if (rec == null) {
    records.putIfAbsent(id, new Record(id));
    rec = records.get(id);
}

在这种方法中,您可能会创建第二个Record对象,但如果是这样,它将不会被插入并立即可用于垃圾回收。截至摘录的末尾:

  • records将包含Record的给定ID
  • 只有一个Record会被放入records以获取该ID(无论是由此线程还是其他线程放入)
  • rec将指向该记录