4

我正在做一个与数据库建立连接的项目。我需要看看有多少次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")
        }
      }
  }

我的问题是 -今天我进行了一次代码审查,我的一位高级团队成员说,你不需要synchronized(this)exceptionMap. catch block我说是的,我们将需要,因为递增计数器是原子的。在映射中添加新值是原子的。但是在没有同步的情况下两者都不是原子的。他说ConurrentHashMap会为你做这件事。

那么我是否需要synchronized(this)阻止它exceptionMap。如果不是那为什么?如果是,那么我应该向他引用什么理由。

4

4 回答 4

0

如果你想计算每个异常发生的次数,那么你需要这样的东西:

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

更新:对您的原作的一些评论:

  • 你是对的,你确实需要另一个包装同步块来确保最新的值实际上进入了地图。但是,正如@Perception 在评论中已经指出的那样,您正在同步错误的对象实例(因为您正在更新静态地图,所以您需要一个静态实例,例如Task.class
  • 但是,您使用的是静态计数器,但 String 键对于不同的异常可能不同,因此您实际上并没有计算每个异常原因,而是将随机数作为各种映射值
  • 最后,正如我在示例中所展示的,您可以通过适当使用 ConcurrentMap 来解决上述问题并完全丢弃同步块。
于 2013-02-12T01:41:21.923 回答
0

这种方式也应该有效。

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)); 
}
于 2013-04-11T06:41:56.193 回答
0

ConcurrentHashMap不允许空值,所以replace如果用oldValue == null. delta我使用此代码通过返回来增加计数器oldValue

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;
}
于 2013-06-21T13:48:15.647 回答
0

您不必使用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;
            });
        }

    }

}
于 2019-09-17T15:57:51.857 回答