我有如下类。我使用ConcurrentHashMap。我有许多线程写入映射表和一个计时器,每5分钟保存一次映射数据。
我通过在写入映射表条目时使用putIfAbsent()来实现线程安全。然而,当我从中读取并使用clear()方法删除所有条目时,我希望没有其他线程在我读取映射内容并将其删除的过程中写入映射表。显然,即使使用synchronized(lock){},我的代码也不是线程安全的,因为在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();
}
}
}
}