2016-02-02 115 views
0

我试图使用多线程来访问Hashtable,因为Hashtable是线程安全的get。但我无法得到它的工作。java多线程访问Hashtable

我认为本地计数器的总和应该等于Hashtable或global_counter的大小。但事实并非如此。

多个线程获取java.util.NoSuchElementException:Hashtable枚举器错误。我认为这个错误是由于枚举Hashtable造成的。是这样吗?

TestMain:

public class TestMain { 

    // MAIN 
    public static void main(String argv[]) throws InterruptedException 
    { 
     Hashtable<Integer, Integer> id2 = new Hashtable<Integer, Integer>(); 
     for (int i = 0; i < 100000; ++i) 
      id2.put(i, i+1); 

     int num_threads = Runtime.getRuntime().availableProcessors() - 1; 
     ExecutorService ExeSvc = Executors.newFixedThreadPool(num_threads); 
     for (int i = 0; i < num_threads; ++i) 
     { 
      ExeSvc.execute(new CalcLink(id2, i)); 
     } 

     ExeSvc.shutdown(); 
     ExeSvc.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); 
    } 
} 

CalcLink:

public class CalcLink implements Runnable { 

    private Hashtable<Integer, Integer> linktable; 
    private static Enumeration keys; 
    private static int global_counter; 
    private int thread_id; 
    private int total_size; 

    public CalcLink(Hashtable<Integer, Integer> lt, int id) 
    { 
     linktable = lt; 
     keys = lt.keys(); 
     thread_id = id; 
     total_size = lt.size(); 
     global_counter = 0; 
    } 

    private synchronized void increment() 
    { 
     ++global_counter; 
    } 

    @Override 
    public void run() 
    { 
     int counter = 0; 
     while (keys.hasMoreElements()) 
     { 
      ++counter; 
      increment(); 
      Integer key = (Integer)keys.nextElement(); 
      Integer value = linktable.get(key); 
     } 

     System.out.println("local counter = " + Integer.toString(counter)); 

     if (thread_id == 1) 
      System.out.println("global counter = " + Integer.toString(global_counter)); 
    } 
} 
+2

每个单独的操作可能是线程安全的,但是,这并不意味着它是线程安全的,做他们一个接一个没有明确持有锁。 (此外,'Hashtable'和'Enumeration'已经几乎在这一点上不建议使用至少15年。) –

+0

不幸的是并发编程是不是可以通过试错来有效地学习(因为我发现我自己成本),我肯定会发现,给出了什么是一个完整的概述一本好书或补习的东西,像Java并发实践,甚至是官方Java教程。 – biziclop

+0

什么是使用多线程读取Hashtable或HashMap中元素的最佳/更好的方法? – stevenhz

回答

0
while (keys.hasMoreElements()) // here you check whether there's an element 
    { 
     ++counter; // other stuff... 
     increment(); // other stuff... 
     Integer key = (Integer)keys.nextElement(); // only here you step 

期间,您是在 “这个主题” 其他的东西,你可以在另一个线程进入其他的东西,因此,恕我直言,你可能会看到更高的数字在全球计数器比你期望的。

这也是您在某些线程中看到NoSuchElementException的原因,它们一起进入“其他内容”,但正在尝试捕获最后一个元素。后来的线程在nextElement()时不会有元素。

0

的问题是,该块不同步:

while (keys.hasMoreElements()) 
{ 
    ++counter; 
    increment(); 
    Integer key = (Integer)keys.nextElement(); 
    Integer value = linktable.get(key); 
} 

keys.hasMoreElements()可以当仍然有在枚举只有一个元素进行评估,以在多线程true。在这些线程:第一个到达keys.nextElement()将被罚款,但所有的人都会提出一个NoSuchElementException

试试这个:

@Override 
public void run() 
{ 
    int counter = 0; 
    synchronized (keys){ 
     while (keys.hasMoreElements()) 
     { 
      ++counter; 
      increment(); 
      Integer key = (Integer)keys.nextElement(); 
      Integer value = linktable.get(key); 
     } 
    } 

    System.out.println("local counter = " + Integer.toString(counter)); 

    if (thread_id == 1) 
     System.out.println("global counter = " + Integer.toString(global_counter)); 
} 
+0

synchoronized(键)没有帮助。 – stevenhz

+0

你还有NoSuchElementException? – ben75

+0

是的。我仍然有一些错误。 – stevenhz

0

一个天真的解决方案:我只是让每个线程处理的长度/ NUM_THREADS记录。只有最后一个线程会处理length/num_threads + length%num_threads记录。