2014-01-06 24 views
1

我创建了一个例子来理解并发HashMap,但它似乎在7-8次运行后给出了不确定的输出。ConcurrentHashMap在多线程应用中没有给出正确的输出

在我的例子中,我创建了三个线程(它们模仿三个服务来获取数学,科学,英语的分数),它更新了一个只有3个键值对的共享HashMap(键是三个名字A,B,C并在运行结束时的值应该是三个科目的累积分数)

我发布了下面的代码,请注意。

错误的输出如下所示(正确的应该结束等来全部完成主:{C = 27,A = 57,B = 42})

Within run Math : {C=0, A=0, B=0} 
Within run Science : {C=0, A=0, B=0} 
Completed Science : {C=10, A=39, B=29} 
Completed Math : {C=10, A=39, B=29} 
Within run English : {C=0, A=0, B=0} 
Completed English : {C=18, A=57, B=42} 
All Done main : {C=18, A=57, B=42} 

ConcurrentHashMapExample类:

package com.ll.thread.concurrency; 

    import java.util.Iterator; 
    import java.util.concurrent.ConcurrentHashMap; 
    import java.util.concurrent.CountDownLatch; 

    public class ConcurrentHashMapExample { 

     public static void main(String[] args) { 
      // TODO Auto-generated method stub 
      ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<String, Integer>(); 
      concurrentHashMap.put("A", 0); 
      concurrentHashMap.put("B", 0); 
      concurrentHashMap.put("C", 0); 
      CountDownLatch countDownLatch = new CountDownLatch(3); 

      Runnable runnableScience = new Worker(concurrentHashMap , "Science" , countDownLatch); 
      Runnable runnableMath = new Worker(concurrentHashMap , "Math" , countDownLatch); 
      Runnable runnableEnglish = new Worker(concurrentHashMap , "English" , countDownLatch); 

      new Thread(runnableScience , "Science").start(); 
      new Thread(runnableMath ,"Math").start(); 
      new Thread(runnableEnglish ,"English").start(); 

      try { 
       countDownLatch.await(); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      System.out.println("All Done " + Thread.currentThread().getName() + " : "+concurrentHashMap); 




      concurrentHashMap = null; 



     } 

    } 

Worker类:​​

package com.ll.thread.concurrency; 

    import java.util.Iterator; 
    import java.util.concurrent.ConcurrentHashMap; 
    import java.util.concurrent.CountDownLatch; 

    public class Worker implements Runnable{ 

     ConcurrentHashMap<String, Integer> concurrentHashMap ; 
     String threadName = ""; 
     CountDownLatch countDownLatch ; 

     public Worker(ConcurrentHashMap<String, Integer> concurrentHashMap, 
       String subject, CountDownLatch countDownLatch) { 
      this.concurrentHashMap = concurrentHashMap; 
      this.threadName = subject ; 
      this.countDownLatch = countDownLatch; 

      // TODO Auto-generated constructor stub 
     } 

     @Override 
     public void run() { 
      // TODO Auto-generated method stub 
      System.out.println("Within run " +Thread.currentThread().getName() + " : "+concurrentHashMap); 
      try{ 
     for(Iterator<String> iterator = concurrentHashMap.keySet().iterator() ; iterator.hasNext();){ 
      String key = iterator.next(); 

      //synchronized (this) { 


      if("Math".equals(Thread.currentThread().getName())) { 
      if("A".equals(key)) 
       concurrentHashMap.put(key, concurrentHashMap.get(key) +20); 
      else if("B".equals(key)) 
       concurrentHashMap.put(key, concurrentHashMap.get(key) + 15); 
      else if("C".equals(key)) 
       concurrentHashMap.put(key, concurrentHashMap.get(key) +10); 
      }else 

      if("Science".equals(Thread.currentThread().getName())) { 
       if("A".equals(key)) 
        concurrentHashMap.put(key, concurrentHashMap.get(key) +19); 
       else if("B".equals(key)) 
        concurrentHashMap.put(key, concurrentHashMap.get(key) +14); 
       else if("C".equals(key)) 
        concurrentHashMap.put(key, concurrentHashMap.get(key) +9); 
       } 
      else 
       if("English".equals(Thread.currentThread().getName())) { 
        if("A".equals(key)) 
         concurrentHashMap.put(key, concurrentHashMap.get(key) +18); 
        else if("B".equals(key)) 
         concurrentHashMap.put(key, concurrentHashMap.get(key) +13); 
        else if("C".equals(key)) 
         concurrentHashMap.put(key, concurrentHashMap.get(key) +8); 
        } 

      } 
     // } 
      } 
      finally{ 

       System.out.println("Completed " + Thread.currentThread().getName() + " : " + concurrentHashMap); 
       countDownLatch.countDown(); 
      } 
     } 
     } 
+4

什么办法? 'Concurrent'(在'ConcurrentHashMap'中)意味着,为了保护其内部结构,映射尽可能地保持同步。这并不意味着,使用它会自动修复使用地图的代码中的竞争条件。 – Dirk

+0

'concurrentHashMap.put(key,concurrentHashMap.get(key)+19);'不是原子的:地图可以在get和put之间更新... – assylias

+0

你没有正确使用它显示的代码!我建议你从阅读接口ConcurrentMap(ConcurrentHashMap是一个实现)的javadocs开始,特别是理解为什么需要新方法 – Scorpion

回答

1

我不认为你在你的测试需要ConcurrentHashMap。您在线程执行之前初始化映射。相反,你可以使用普通HashMap通过String键和AtomicInteger值参数化:

HashMap<String, AtomicInteger> concurrentHashMap = ... 
concurrentHashMap.put("A", new AtomicInteger(0)); 
... 

然后你就可以原子增加每个线程AtomicInteger实例的值:

concurrentHashMap.get(key).addAndGet(20); 
相关问题