【问题标题】:java multi threads access Hashtablejava多线程访问Hashtable
【发布时间】:2016-02-02 20:55:04
【问题描述】:

我尝试使用多线程来访问 Hashtable,因为 Hashtable 在获取时是线程安全的。但我无法让它工作。

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

多个线程得到 java.util.NoSuchElementException: Hashtable Enumerator 错误。我认为错误是由于 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);
    }
}

计算链接:

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));
    }
}

【问题讨论】:

  • 每个单独的操作可能是线程安全的,但这并不意味着在没有明确持有锁的情况下一个接一个地执行它们是线程安全的。 (此外,HashtableEnumeration 目前已被弃用至少 15 年。)
  • 不幸的是并发编程不是可以通过反复试验有效地学习的东西(正如我自己发现的那样),我肯定会找到一本提供完整概述的好书或教程什么是什么,例如 Java Concurrency in Practice,甚至是官方 Java 教程。
  • 使用多线程从 Hashtable 或 HashMap 读取元素的最佳/更好的方法是什么?
  • @biziclop,感谢您的建议

标签: java multithreading hashtable


【解决方案1】:
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(); 时不会有该元素;

【讨论】:

    【解决方案2】:

    问题是这个块没有同步:

    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));
    }
    

    【讨论】:

    • synchoronized(keys) 没有帮助。
    • 你还有 NoSuchElementException 吗?
    • NoSuchElementException 还是别的什么?
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-17
      • 2014-04-04
      • 1970-01-01
      • 2013-08-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多