问题
I use Java 8. I have an event handler that accepts events with a high rate, (n per second) and I want to flush them out to storage when I get so many of them (in this simplified example 1000)
Do I have a visibility error on line 25 myCache.get(event.getKey()).add(event.getBean());
?
Should I synchronize on handleEvent()
method?
public class myClass extends MySimpleEventHanlder {
private Map<String, List<MyBean>> myCache;
private ScheduledExecutorService scheduler;
public void MyClass() {
myCache = new ConcurrentHashMap<String, List<MyBean>>();
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
for (Iterator<Map.Entry<String, List<MyBean>>> it = myCache.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, List<MyBean>> entry = it.next();
if (entry.getValue().size() >= 1000) {
it.remove();
//do some more processing , flush to storage
}
}
}, 0, 60, TimeUnit.SECONDS);
}
@Override
public void handleEvent(Event event) {
if (myCachetCache.containsKey(event.getKey())) {
myCache.get(event.getKey()).add(event.getBean());
}
else{
List<MyBean> beans = new ArrayList<MyBeans>();
beans.add(event.getBean());
myCache.put(event.key, beans);
}
}
}
回答1:
You definitely have visibility problems: you add items into an ArrayList in one thread, and read the size() from that ArrayList in another thread, with no synchronization in between.
Another problem is that the key may get removed between the calls to myCache.containsKey
and myCache.get
. This would cause a NullPointerException. That could be solved by using compute, which is guaranteed to be atomic.
myCache.compute(event.getKey(), (key, value) -> {
if (value == null) {
value = new ArrayList<>();
}
value.add(event.getBean());
return value;
});
来源:https://stackoverflow.com/questions/60834919/resolving-java-thread-visibility-and-concurrency-error-using-map-compute