【发布时间】:2012-08-20 09:23:36
【问题描述】:
我有以下课程。我使用 ConcurrentHashMap。我有许多线程写入地图和一个计时器,每 5 分钟将数据保存在地图中。 当我在映射中写入条目时,我设法通过使用 putIfAbsent() 来实现线程安全。但是,当我从中读取然后通过 clear() 方法删除所有条目时,我希望在读取映射内容然后删除它们的过程中没有其他线程写入映射。显然,即使使用了同步(锁定){},我的代码也不是线程安全的,b/c 在 saveEntries() 中拥有锁的线程不一定是在 log() 方法中写入我的地图的同一线程!除非我用同一个锁对象锁定 log() 中的整个代码!
我想知道是否有任何其他方法可以在不通过外部锁强制同步的情况下实现线程安全?非常感谢任何帮助。
public class Logging {
private static Logging instance;
private static final String vendor1 = "vendor1";
private static final String vendor2 = "vendor2";
private static long delay = 5 * 60 * 1000;
private ConcurrentMap<String, Event> vendor1Calls = new ConcurrentHashMap<String, Event>();
private ConcurrentMap<String, Event> vendor2Calls = new ConcurrentHashMap<String, Event>();
private Timer timer;
private final Object lock = new Object();
private Logging(){
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
try {
saveEntries();
} catch (Throwable t) {
timer.cancel();
timer.purge();
}
}
}, 0, delay);
}
public static synchronized Logging getInstance(){
if (instance == null){
instance = new Logging();
}
return instance;
}
public void log(){
ConcurrentMap<String, Event> map;
String key = "";
if (vendor1.equalsIgnoreCase(engine)){
map = vendor1Calls;
}else if(vendor2.equalsIgnoreCase(engine)){
map = vendor2Calls;
}else{
return;
}
key = service + "." + method;
// It would be the code if I use a regular HashMap instead of ConcurrentHashMap
/*Event event = map.get(key);
// Map does not contain this service.method, create an Event for the first time.
if(event == null){
event = new Event();
map.put(key, event);
// Map already contains this key, just adjust the numbers.
}else{
// Modify the object fields
}*/
//}
// Make it thread-safe using CHM
Event newEvent = new Event();
Event existingEvent= map.putIfAbsent(key, newEvent);
if(existingEvent!=null && existingEvent!=newEvent){
// Modify the object fields
}
private void saveEntries(){
Map<String, List<Event>> engineCalls = null;
try {
engineCalls = new HashMap<String, List<Event>>();
List<Event> events = null;
// How can I achieve therad safety here w/o applying any lock?
//synchronized(lock){
if(!vendor1Calls.isEmpty()){
events = new ArrayList<Event>();
events.addAll(vendor1Calls.values());
engineCalls.put(vendor1, events);
vendor1Calls.clear();
}
if(!vendor2Calls.isEmpty()){
events = new ArrayList<Event>();
events.addAll(vendor2Calls.values());
engineCalls.put(vendor2, events);
vendor2Calls.clear();
}
//}
// logICalls() saves the events in the DB.
DBHandle.logCalls(engineCalls);
} catch (Throwable t) {
} finally {
if(engineCalls!=null){
engineCalls.clear();
}
}
}
}
【问题讨论】:
-
您通过单击答案左侧的灰色复选标记来接受答案。如果答案不能解决您的问题,请不要接受,尽管有同侪压力。
-
是的,我建议你去stackoverflow.com/users/1052348/bluesky?tab=questions,点击每个链接,然后点击最能解决你问题的答案旁边的复选标记。 PS您可以接受您自己对自己的问题做出的回答。
-
crossposted here,您可以单独使用 CHM 功能使其具有原子性。
-
我知道这是一个老问题,但我不明白您为什么首先将地图用于看起来本质上像队列的东西。您可以将日志条目添加到并发队列中,然后在保存条目中查看队列的大小并从队列的前面删除那么多并写入它们。因为您只看一次大小,所以在保存时添加更多也没关系;他们将留给下次。对不起,如果我误解了你的目的。
标签: java multithreading concurrency thread-safety