2016-02-02 117 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記錄。