[英]Putting the value in ConcurrentHashMap is atomic or not?
我正在做一个与数据库建立连接的项目。 我需要查看exception is happening
次数exception is happening
如果有)。 我正在使用Multithreaded code
,这意味着多个线程将连接到数据库并插入数据库。 因此,在某些时候连接可能会丢失,因此我们需要查看这些异常发生了多少次。
因此,我编写了下面的代码,在catch块中,我捕获了异常,并在每次出现异常时都对增加计数进行计数并将其放入ConcurrentHashMap
。
class Task implements Runnable {
public static final AtomicInteger counter_sql_exception = new AtomicInteger(0);
public static final AtomicInteger counter_exception = new AtomicInteger(0);
public static ConcurrentHashMap<String, Integer> exceptionMap = new ConcurrentHashMap<String, Integer>();
@Override
public void run() {
try {
//Make a db connection and then executing the SQL-
} catch (SQLException e) {
synchronized(this) {
exceptionMap.put(e.getCause().toString(), counter_sql_exception.incrementAndGet());
}
LOG.Error("Log Exception")
} catch (Exception e) {
synchronized(this) {
exceptionMap.put(e.getCause().toString(), counter_exception.incrementAndGet());
}
LOG.Error("Log Exception")
}
}
}
我的问题是-今天我进行了代码审查,我的一位高级团队成员说,您将不需要在catch block
的exceptionMap
上synchronized(this)
。 我说是的,因为计数器的增加是原子的,所以我们将需要它。 在地图中放置一个新值是原子的。 但是,在没有同步的情况下进行这两者并不是原子的。 他说ConurrentHashMap
将为您做到这一点。
那么我是否需要在该exceptionMap
上使用exceptionMap
synchronized(this)
块? 如果没有,那为什么呢? 如果是,那我应该引用什么理由。
如果要统计每个异常的发生次数,则需要这样的操作:
private static final ConcurrentMap<String, AtomicInteger> exceptionMap = new ConcurrentHashMap<String, AtomicInteger>();
private static void addException(String cause) {
AtomicInteger count = exceptionMap.get(cause);
if(count == null) {
count = new AtomicInteger();
AtomicInteger curCount = exception.putIfAbsent(cause, count);
if(curCount != null) {
count = curCount;
}
}
count.incrementAndGet();
}
请注意,除非定期清除,否则具有静态的异常映射是资源泄漏。
如@dnault所述,您还可以使用番石榴的AtomicLongMap 。
更新:对您的原件的一些评论:
Task.class
)。 而且这种方式也应该起作用。
private static final ConcurrentMap<String, Integer> exceptionMap = new ConcurrentHashMap<String, Integer>();
private static void addException(String cause) {
Integer oldVal, newVal;
do {
oldVal = exceptionMap .get(cause);
newVal = (oldVal == null) ? 1 : (oldVal + 1);
} while (!queryCounts.replace(q, oldVal, newVal));
}
ConcurrentHashMap
不允许使用null值,因此如果使用oldValue == null
调用,则replace
将引发异常。 我使用此代码通过返回oldValue
增加计数器的delta
。
private final ConcurrentMap<Integer,Integer> counters = new ConcurrentHashMap<Integer,Integer>();
private Integer counterAddDeltaAndGet(Integer key, Integer delta) {
Integer oldValue = counters.putIfAbsent(key, delta);
if(oldValue == null) return null;
while(!counters.replace(key, oldValue, oldValue + delta)) {
oldValue = counters.get(key);
}
return oldValue;
}
您不必使用synchronized
块和AtomicInteger
。 您可以使用ConcurrentHashMap.compute
方法来执行此操作,该方法是线程安全的原子操作。 所以你的代码看起来像这样
public class Task implements Runnable {
public static final Map<String, Integer> EXCEPTION_MAP = new ConcurrentHashMap<String, Integer>();
@Override
public void run() {
try {
// Make a db connection and then executing the SQL-
} catch (Exception e) {
EXCEPTION_MAP.compute(e.getCause().toString(), (key, value) -> {
if (value == null) {
return 1;
}
return ++value;
});
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.