简体   繁体   English

将值放在ConcurrentHashMap中是否是原子的?

[英]Putting the value in ConcurrentHashMap is atomic or not?

I am working on a project in which I am making connections to database. 我正在做一个与数据库建立连接的项目。 And I need to see how many times an exception is happening if there are any. 我需要查看exception is happening次数exception is happening如果有)。 I am working with Multithreaded code , meaning multiple threads will be making connection to database and inserting into database. 我正在使用Multithreaded code ,这意味着多个线程将连接到数据库并插入数据库。 So it might be possible that at some point connection get lost so we need to see how many times those exception has occurred. 因此,在某些时候连接可能会丢失,因此我们需要查看这些异常发生了多少次。

So I wrote a below code and in the catch block, I am catching exception and making a counter to increased every time if there is any exeption and putting it in ConcurrentHashMap . 因此,我编写了下面的代码,在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")
        }
      }
  }

My Question is- Today I had a code review and one of my senior team members said, you won't be needing synchronized(this) on the exceptionMap in the catch block . 我的问题是-今天我进行了代码审查,我的一位高级团队成员说,您将不需要在catch blockexceptionMapsynchronized(this) I said yes we will be needing because incrementing the counter is atomic. 我说是的,因为计数器的增加是原子的,所以我们将需要它。 Putting a new value in the map is atomic. 在地图中放置一个新值是原子的。 But doing both without synchronization is not atomic . 但是,在没有同步的情况下进行这两者并不是原子的。 And he said ConurrentHashMap will be doing this for you. 他说ConurrentHashMap将为您做到这一点。

So does I will be needing synchronized(this) block on that exceptionMap or not.? 那么我是否需要在该exceptionMap上使用exceptionMap synchronized(this)块? If not then why? 如果没有,那为什么呢? And if Yes then what reason should I quote to him. 如果是,那我应该引用什么理由。

if you are tying to count the number of times each exception occured, then you need something like 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();
}

note that having a static map of exceptions is a resource leak unless you periodically clean it out. 请注意,除非定期清除,否则具有静态的异常映射是资源泄漏。

As @dnault mentioned, you could also use guava's AtomicLongMap . 如@dnault所述,您还可以使用番石榴的AtomicLongMap

UPDATE: some comments on your original: 更新:对您的原件的一些评论:

  • you are correct, that you do need another wrapping synchronized block to ensure that the latest value actually makes it into the map. 您是正确的,您确实需要另一个包装同步块来确保最新值确实进入了映射。 however, as @Perception already pointed out in comments, you are synchronizing on the wrong object instance (since you are updating static maps, you need a static instance, such as Task.class ) 但是,正如@Perception在注释中已经指出的那样,您正在错误的对象实例上进行同步(由于要更新静态映射,因此需要一个静态实例,例如Task.class )。
  • however, you are using a static counter, but a String key which could be different for different exceptions, thus you aren't actually counting each exception cause, but instead sticking random numbers in as various map values 但是,您使用的是静态计数器,但是String键对于不同的异常可能会有所不同,因此您实际上并没有在计算每个异常的原因,而是将随机数作为各种映射值插入
  • lastly, as i've shown in my example, you can solve the aforementioned issues and discard the synchronized blocks completely by making appropriate use of the ConcurrentMap. 最后,如我的示例所示,可以通过适当地使用ConcurrentMap来解决上述问题并完全丢弃同步块。

And this way should also work. 而且这种方式也应该起作用。

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 doesn't allow null values, so replace will throw exception if called with oldValue == null . ConcurrentHashMap不允许使用null值,因此如果使用oldValue == null调用,则replace将引发异常。 I used this code to increase counter by delta with returning oldValue . 我使用此代码通过返回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;
}

You don't have to use synchronized block and AtomicInteger . 您不必使用synchronized块和AtomicInteger You can do it just with ConcurrentHashMap.compute method which is a thread-safe atomic operation. 您可以使用ConcurrentHashMap.compute方法来执行此操作,该方法是线程安全的原子操作。 So your code will look something like this 所以你的代码看起来像这样

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM