Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/database/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 将值放入ConcurrentHashMap是否是原子的?_Java_Database_Multithreading_Concurrency_Atomicity - Fatal编程技术网

Java 将值放入ConcurrentHashMap是否是原子的?

Java 将值放入ConcurrentHashMap是否是原子的?,java,database,multithreading,concurrency,atomicity,Java,Database,Multithreading,Concurrency,Atomicity,我正在做一个项目,在这个项目中我正在和数据库建立连接。我需要看看一个异常发生了多少次(如果有)。我正在使用多线程代码,这意味着多个线程将连接到数据库并插入数据库。所以可能在某个时刻连接丢失,所以我们需要看看这些异常发生了多少次 所以我写了一段下面的代码,在catch块中,我捕获异常,并在每次有任何exeption时生成一个递增计数器,并将其放入ConcurrentHashMap class Task implements Runnable { public static final

我正在做一个项目,在这个项目中我正在和数据库建立连接。我需要看看一个
异常发生了多少次
(如果有)。我正在使用
多线程代码
,这意味着多个线程将连接到数据库并插入数据库。所以可能在某个时刻连接丢失,所以我们需要看看这些异常发生了多少次

所以我写了一段下面的代码,在catch块中,我捕获异常,并在每次有任何exeption时生成一个递增计数器,并将其放入
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")
        }
      }
  }
类任务实现可运行{
公共静态最终AtomicInteger计数器\u sql\u异常=新的AtomicInteger(0);
公共静态最终AtomicInteger计数器\u异常=新的AtomicInteger(0);
public static ConcurrentHashMap exceptionMap=新ConcurrentHashMap();
@凌驾
公开募捐{
试一试{
//建立数据库连接,然后执行SQL-
}捕获(SQLE异常){
已同步(此){
exceptionMap.put(例如getCause().toString(),counter_sql_exception.incrementAndGet());
}
LOG.Error(“日志异常”)
}捕获(例外e){
已同步(此){
exceptionMap.put(例如getCause().toString(),counter_exception.incrementAndGet());
}
LOG.Error(“日志异常”)
}
}
}
我的问题是-今天我进行了一次代码审查,我的一位高级团队成员说,您不需要在
捕获块的
异常映射上进行
同步(此)
。我说是的,我们将需要,因为递增计数器是原子的。在映射中放置新值是原子的。但是,不同步地同时做这两件事并不是原子的。他说,
ConurrentHashMap
将为你做这件事


那么我是否需要在该
异常映射上进行
同步(此)
块呢。?若否,原因为何?如果是,那么我应该向他说明什么原因。

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

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();
}
private static final ConcurrentMap exceptionMap=new ConcurrentHashMap();
私有静态void addException(字符串原因){
AtomicInteger count=exceptionMap.get(原因);
如果(计数=null){
count=新的原子整数();
AtomicInteger curCount=异常。putIfAbsent(原因,计数);
if(curCount!=null){
计数=电流计数;
}
}
count.incrementAndGet();
}
请注意,除非定期清除异常,否则拥有异常的静态映射是一种资源泄漏

正如@dnault所提到的,您也可以使用

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

  • 您是正确的,您确实需要另一个包装同步块来确保最新的值实际进入映射。但是,正如@Perception在评论中指出的,您在错误的对象实例上进行同步(因为您正在更新静态映射,所以您需要一个静态实例,例如
    Task.class
  • 然而,您使用的是一个静态计数器,但字符串键对于不同的异常可能不同,因此您实际上并没有计算每个异常原因,而是将随机数作为各种映射值插入
  • 最后,如我在示例中所示,您可以通过适当使用ConcurrentMap来解决上述问题并完全丢弃同步块

    • 这种方法也应该有效

      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)); 
      }
      
      private static final ConcurrentMap exceptionMap=new ConcurrentHashMap();
      私有静态void addException(字符串原因){
      整数oldVal,newVal;
      做{
      oldVal=exceptionMap.get(原因);
      newVal=(oldVal==null)?1:(oldVal+1);
      }而(!queryCounts.replace(q,oldVal,newVal));
      }
      
      ConcurrentHashMap
      不允许空值,因此如果使用
      oldValue==null
      调用
      replace
      将引发异常。我使用此代码将计数器增加
      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;
      }
      
      私有最终ConcurrentMap计数器=新ConcurrentHashMap();
      私有整数计数器addDeltaAndGet(整数键,整数增量){
      整数oldValue=计数器。putIfAbsent(键,增量);
      如果(oldValue==null)返回null;
      while(!counters.replace(键、旧值、旧值+增量)){
      oldValue=计数器。获取(键);
      }
      返回旧值;
      }
      
      您不必使用
      同步
      块和
      原子整数
      。只需使用
      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;
                  });
              }
      
          }
      
      }
      
      公共类任务实现可运行{
      公共静态最终映射异常_Map=new ConcurrentHashMap();
      @凌驾
      公开募捐{
      试一试{
      //建立数据库连接,然后执行SQL-
      }捕获(例外e){
      异常映射.compute(例如getCause().toString(),(键,值)->{
      如果(值==null){
      返回1;
      }
      返回++值;
      });
      }
      }
      }
      
      您是在尝试统计所有正在发生的异常,还是每个异常发生的次数?每个异常发生的次数。那么为什么要使用一个共享计数器?很抱歉,让我更新我的问题。我没有使用共享计数器。当我发布问题时,我错误地复制了唯一的计数器。只是更新了问题。谢谢jtahlborn的建议。这意味着我可以把上面的方法调用放在catch块中,对吗
      catch(异常e){addException(e.getCause().toString());}
      像这样吗?参见番石榴