我制作了一个程序来计算单个文件中的单词,但是我该如何修改我的程序,因此它给出了所有文件中的单词总数(作为一个值)。
我的代码如下:
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();
}
}
}
以及如何避免比赛条件?谢谢您的帮助。
工作线程:
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);
}
}
许多硬盘驱动器不能很好地同时读取多个文件。引用的位置对性能有很大影响。
[您既可以使用Future获取计数,最后累加所有计数,也可以使用静态变量并以synchronized
方式对其进行递增,即明确使用synchronized
或使用Atomic Increment
如果您的Runnable
接受两个参数怎么办:
BlockingQueue<String>
或BlockingQueue<File>
在循环中,您将从队列中获取下一个字符串/文件,计算其字数,然后将AtomicLong
增加该数量。循环是while(!queue.isEmpty())
还是while(!done)
取决于将文件送入队列的方式:如果从一开始就知道所有文件,则可以使用isEmpty
版本,但是如果要从某个位置将其流式传输,您想使用!done
版本(并让done
成为volatile boolean
或AtomicBoolean
以提高内存可见性)。
然后您将这些Runnable
馈给执行者,您应该会很好。
您可以创建一些侦听器以从线程中获取反馈。
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();
}
}
}
您可以制作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
。
您可以创建一个带有同步任务队列的线程池,该任务池将保存您要为其计算单词的所有文件。
当线程池工作者在线时,他们可以向任务队列请求要计数的文件。工人完成工作后,他们可以将其最终号码通知主线程。
主线程将具有一个同步的notify方法,它将所有工作线程的结果加起来。
希望这会有所帮助。
或者您可以让所有线程更新一个单词计数变量。如果count是字面的,则count ++是原子的(int应该足够)。
编辑:事实证明Java规范足够愚蠢,以至于count ++是not原子的。我不知道为什么。无论如何,请看一下AtomicInteger及其递增AndGet方法。希望这个is原子(我现在不知道要期待什么...),并且您不需要任何其他同步机制-只需将您的计数存储在AtomicInteger中即可。
我如何从此代码读取多个文件,我可以在其中放置要读取的实际文件名?我应该把第二个文件名放在哪里?