combine putIfAbsent and replace with ConcurrentMap

后端 未结 4 2070
长情又很酷
长情又很酷 2021-02-04 03:54

I have a usecase where I have to

  • insert a new value if the key does not exist in the ConcurrentHashMap
  • replace the old value with a new value if the key a
4条回答
  •  北海茫月
    2021-02-04 04:19

    I don´t think it´s correct. As I understand it the merge() method would be the right tool for the job. I currently have the same problem and wrote a litte test to see the results.

    This test starts 100 workers. Each of them is incrementing the value in the map 100 times. So the expected result would be 10000.

    There are two types of workers. One that uses the replace algorithm and on that uses merge. The test is run two times with the different implementations.

    import java.util.concurrent.ArrayBlockingQueue;                                                                     
    import java.util.concurrent.ConcurrentHashMap;                                                                      
    import java.util.concurrent.ConcurrentMap;                                                                          
    import java.util.concurrent.ExecutorService;                                                                        
    import java.util.concurrent.ThreadPoolExecutor;                                                                     
    import java.util.concurrent.TimeUnit;                                                                               
    
    public class ConcurrentMapTest                                                                                      
    {                                                                                                                   
    
       private static ConcurrentMap map = new ConcurrentHashMap<>();                                   
    
       private final class ReplaceWorker implements Runnable                                                            
       {                                                                                                                
          public void run()                                                                                             
          {                                                                                                             
             for(int i = 0; i<100; i++)                                                                                 
             {                                                                                                          
                Integer putIfAbsent = map.putIfAbsent("key", Integer.valueOf(1));                                       
                if(putIfAbsent == null)                                                                                 
                   return;                                                                                              
                map.replace("key", putIfAbsent + 1);                                                                    
             }                                                                                                          
          }                                                                                                             
       }                                                                                                                
    
       private final class MergeWorker implements Runnable                                                              
       {                                                                                                                
          public void run()                                                                                             
          {                                                                                                             
             for(int i = 0; i<100; i++)                                                                                 
             {                                                                                                          
                map.merge("key", Integer.valueOf(1), (ov, nv) -> {                                                      
                   return ov + 1;                                                                                       
                });                                                                                                     
             }                                                                                                          
          }                                                                                                             
       }                                                                                                                
    
       public MergeWorker newMergeWorker()                                                                              
       {                                                                                                                
          return new MergeWorker();                                                                                     
       }                                                                                                                
    
       public ReplaceWorker newReplaceWorker()                                                                          
       {                                                                                                                
          return new ReplaceWorker();                                                                                   
       }                                                                                                                
    
       public static void main(String[] args)                                                                           
       {                                                                                                                
          map.put("key", 1);                                                                                            
          ConcurrentMapTest test = new ConcurrentMapTest();                                                             
          ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new ArrayBlockingQu
          for(int i = 0; i<100; i++)                                                                                    
          {                                                                                                             
             threadPool.submit(test.newMergeWorker());                                                                  
          }                                                                                                             
          awaitTermination(threadPool);                                                                                 
          System.out.println(test.map.get("key"));                                                                      
    
          map.put("key", 1);                                                                                            
          threadPool = new ThreadPoolExecutor(10, 10, 100, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1000));      
          for(int i = 0; i<100; i++)                                                                                    
          {                                                                                                             
             threadPool.submit(test.newReplaceWorker());                                                                
          }                                                                                                             
          awaitTermination(threadPool);                                                                                 
          System.out.println(test.map.get("key"));                                                                      
       }                                                                                                                
    
       private static void awaitTermination(ExecutorService threadPool)                                                 
       {                                                                                                                
          try                                                                                                           
          {                                                                                                             
             threadPool.shutdown();                                                                                     
             boolean awaitTermination = threadPool.awaitTermination(1, TimeUnit.SECONDS);                               
             System.out.println("terminted successfull: " + awaitTermination);                                          
          }                                                                                                             
          catch (InterruptedException e)                                                                                
          {                                                                                                             
             // TODO Auto-generated catch block                                                                         
             e.printStackTrace();                                                                                       
          }                                                                                                             
       }                                                                                                                
    }                                                                                          
    
    result:
    terminted successfull: true
    10000
    terminted successfull: true
    1743
    

    The problem is that there is a gap between the get and the put in your case, so with concurrent accsess to the map results get overwritten. With merge it´s an atomic operation although the documentation does not say anything about it.

提交回复
热议问题