4

我制作了一个程序来计算单个文件中的单词,但是我如何修改我的程序,所以它给出了所有文件中的单词总数(作为一个值)。

我的代码如下所示:

public class WordCount implements Runnable
{
   public WordCount(String filename)
   {
      this.filename = filename;
   }

   public void run()
   {
      int count = 0;
      try
      {
         Scanner in = new Scanner(new File(filename));

         while (in.hasNext())
         {
            in.next();
            count++;
         }
         System.out.println(filename + ": " + count);
      }
      catch (FileNotFoundException e)
      {
         System.out.println(filename + " blev ikke fundet.");
      }
   }
   private String filename;
}

使用主类:

public class Main
{

   public static void main(String args[])
   {
      for (String filename : args)
      {
         Runnable tester = new WordCount(filename);

         Thread t = new Thread(tester);
         t.start();
      }
   }
}

以及如何避免竞争条件?谢谢您的帮助。

4

8 回答 8

3

一个工作线程:

class WordCount extends Thread
{

   int count;

   @Override
   public void run()
   {
      count = 0;
      /* Count the words... */
      ...
      ++count;
      ...
   }

}

还有一个使用它们的类:

class Main
{

   public static void main(String args[]) throws InterruptedException
   {
      WordCount[] counters = new WordCount[args.length];
      for (int idx = 0; idx < args.length; ++idx) {
         counters[idx] = new WordCount(args[idx]);
         counters[idx].start();
      }
      int total = 0;
      for (WordCount counter : counters) {
        counter.join();
        total += counter.count;
      }
      System.out.println("Total: " + total);
   }

}

许多硬盘不能很好地同时读取多个文件。参考位置对性能有很大影响。

于 2011-12-08T22:36:18.627 回答
1

您可以使用Future获取计数并最终将所有计数相加,或者使用静态变量并以某种synchronized方式递增它,即显式使用synchronized或使用原子增量

于 2011-12-08T22:22:15.143 回答
1

如果你Runnable有两个论点怎么办:

  • 一个BlockingQueue<String>BlockingQueue<File>一个输入文件
  • AtomicLong

在一个循环中,您将从队列中获取下一个字符串/文件,计算其字数,然后AtomicLong按该数量递增。循环是否while(!queue.isEmpty())while(!done)取决于您将文件输入队列的方式:如果您从一开始就知道所有文件,则可以使用该isEmpty版本,但如果您从某个地方将它们流式传输,则要使用该!done版本(并且有done一个volatile booleanAtomicBoolean用于内存可见性)。

然后你将这些Runnables 提供给一个执行者,你应该很高兴。

于 2011-12-08T22:27:41.083 回答
1

您可以使count volatile所有static线程都可以增加它。

public class WordCount implements Runnable
{
   private static AtomicInteger count = new AtomicInteger(0); // <-- now all threads increment the same count

   private String filename;

   public WordCount(String filename)
   {
      this.filename = filename;
   }

   public static int getCount()
   {
       return count.get();
   }

   public void run()
   {
      try
      {
         Scanner in = new Scanner(new File(filename));

         while (in.hasNext())
         {
            in.next();
            count.incrementAndGet();
         }
         System.out.println(filename + ": " + count);
      }
      catch (FileNotFoundException e)
      {
         System.out.println(filename + " blev ikke fundet.");
      }
   }
}

更新:有一段时间没有做 java 了,但将其设为私有静态字段的意义仍然存在……只需将其设为AtomicInteger.

于 2011-12-08T22:28:21.190 回答
1

您可以创建一些侦听器以从线程中获取反馈。

   public interface ResultListener {
       public synchronized void result(int words);
   }
   private String filename;
   private ResultListener listener;
   public void run()
   {
     int count = 0;
     try
     {
       Scanner in = new Scanner(new File(filename));

       while (in.hasNext())
       {
          in.next();
          count++;
       }
       listener.result(count); 
    }
    catch (FileNotFoundException e)
    {
       System.out.println(filename + " blev ikke fundet.");
    }
   }
  }

您可以为侦听器添加一个构造函数参数,就像为您的文件名一样。

  public class Main
  {
     private static int totalCount = 0;
     private static ResultListener listener = new ResultListener(){
         public synchronized void result(int words){
            totalCount += words;
         }
     }
     public static void main(String args[])
     {
        for (String filename : args)
        {
           Runnable tester = new WordCount(filename, listener);

           Thread t = new Thread(tester);
           t.start();
        }
     }
  }
于 2011-12-08T22:28:38.440 回答
0

您可以创建一个带有同步任务队列的线程池,该队列将保存您希望计算字数的所有文件。

当您的线程池工作人员上线时,他们可以向任务队列询问要计数的文件。工人完成工作后,他们可以通知主线程他们的最终数字。

主线程将有一个同步的通知方法,它将所有工作线程的结果相加。

希望这可以帮助。

于 2011-12-08T22:21:05.100 回答
0

或者,您可以让所有线程更新单个字数变量。如果 count 是单字的,则 count++ 是原子的(一个 int 就足够了)。

编辑:原来 Java 规范只是愚蠢到 count++不是原子的。我不知道为什么。无论如何,看看 AtomicInteger 及其 incrementAndGet 方法。希望这原子的(我现在不知道会发生什么......),并且您不需要任何其他同步机制 - 只需将您的计数存储在 AtomicInteger 中。

于 2011-12-08T22:28:30.090 回答
0

考虑到 Java8 并发包,涉及 Executors 和 Future 用于多线程,共享给定的解决方案。

首先,为处理单个文件而创建的可调用类

public class WordCounter implements Callable {

Path bookPath;

public WordCounter(Path bookPath) {
    this.bookPath = bookPath;
}

@Override
public Map<String, Long> call() throws Exception {
    Map<String, Long> wordCount = new HashMap<>();
    wordCount = Files.lines(bookPath).flatMap(line -> Arrays.stream(line.trim().split(" ")).parallel())
               .map(word -> word.replaceAll("[^a-zA-Z]", "").toLowerCase().trim())
               .filter(word -> word.length() > 0)
               .map(word -> new SimpleEntry<>(word, 1))
               .collect(Collectors.groupingBy(SimpleEntry::getKey, Collectors.counting()));     
    return wordCount;
}
}

现在,我们将创建多个未来任务来调用/处理参数中的每个文件,如下所示

ExecutorService exes = Executors.newCachedThreadPool();
FutureTask[] tasks = new FutureTask[count];
Map<String, Long> result = new HashMap<>();

Path[] books = new Path[2];
books[0] = Paths.get("C:\\Users\\Documents\\book1.txt");
books[1] = Paths.get("C:\\Users\\Documents\\book2.txt");
    
   for(int i=0; i<books.length; i++) {
        tasks[i] = new FutureTask(new WordCounter(books[i]));
        exes.submit(tasks[i]);
    }
    
    for(int i=0; i<count; i++) {
        try {
            Map<String, Long> wordCount = (Map<String, Long>) tasks[i].get();
            wordCount.forEach((k,v) -> result.put(k, result.getOrDefault(k, 0L)+1));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
    exes.shutdown();

进一步result的 map 可以升级为 volatile 关键字并在WordCounter线程之间共享以同时更新字数。

最终结果:result.size()应该给出预期的输出

于 2021-08-10T08:57:43.543 回答