2013-09-29 95 views
0

下面的代码有什么问题?下面的代码不是线程安全的吗?

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. 锁定仅适用于更新。在检索的情况下,其 允许完全并发。这个说法是什么意思?

回答

6

这不是线程安全的。

  1. 如果有另一个线程,然后在records.getrecords.put之间的时间另一个线程可能已经把记录为好。

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

我知道,这听起来像一个非常不太可能发生,但是请记住,在一百万1事件为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而从不使用它。

+0

只读操作(即不修改结构的操作)可以由多个线程同时完成。 - 这是否意味着完全并发? –

+0

我以前没有听说过这个词,但在我看来,是的。 – Adam

+0

用putIfAbsent()替换put的最后一个问题是否使线程安全? –

3

它可能或不可能是线程安全的,这取决于你希望如何行动。

在代码结束时,aMap将安全地拥有Recordid。但是,有可能两个线程都会在中创建并放入一个Record,这样有两个(或更多,如果更多的线程这样做)Records存在。这可能是好的,也可能不是 - 真的取决于你的应用程序。

线程安全的危险之一(例如,如果您使用正常的HashMap而没有同步)是线程可以跨线程读取部分创建或部分更新的对象;换句话说,事情可以去真的干扰。这将在您的代码中发生而不是,因为ConcurrentHashMap将确保内存在线程之间保持最新,并且从这个意义上讲,它是线程安全的。

一两件事你可以做的是使用putIfAbsent,这将原子放在一个键值对到地图,但前提是没有什么那个键已经:

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

在这种方法中,你可能会创建另一个Record对象,但如果是这样,它将不会被插入,并立即可用于垃圾回收。由段的末尾:

  • records将包含给定id
  • 只有一个Record将曾经被投入records该ID的Record(无论是通过这个线程放在那里或其他)
  • rec将指向该记录
+0

我认为你的意思是'records.putIfAbsent(..'而不是'records.put(..'在你的代码片段 – Adam

+0

哎呀,是的,谢谢你。 – yshavit

+1

谢谢@yshavit你的回答...谢谢你的时间。 –

相关问题