我的任务是使用Java中的多线程计算文本文件中字符的频率。目的是评估该任务是可以顺序执行还是并行处理更快完成。
下面的代码在使用单个线程运行时会产生正确的答案,因此我相信逻辑是正确的。当我使用两个线程时,出现此错误,我不知道该怎么办。我搜索了这个问题,并得出了一个独立的线程是值得的。请帮助,让我知道我忘记提及的其他详细信息。非常感谢!
{正在使用jdk 10。}
代码:
SimpleArray类
import java.util.Arrays;
public class SimpleArray {
private final int[] array;
public SimpleArray(int size)
{
array = new int[size];
}
public synchronized void add (String word)
{
for(int i = 0; i < word.length(); i++)
{
char c = word.charAt(i);
int place = getInt(c);
if(place < 0 || place > 25)
{
//Non characters are ignored.
}
else
{
try
{
array[place]++;
}
catch(IndexOutOfBoundsException ex)
{
System.err.println("Despite control, out of bound in array.");
}
}
}
}
private int getInt(char c)
{
int ascii = (int) c;
if(ascii >= 65 && ascii <= 90)
{
return (ascii % 65);
}
else if (ascii >= 97 && ascii <= 122)
{
return (ascii % 97);
}
else
{
return -1;
}
}
//used for outputting the content of shared integer array
public synchronized String toString()
{
return Arrays.toString(array);
}
}
ArrayWriter类
import java.lang.Runnable;
import java.util.NoSuchElementException;
import java.util.Scanner;
public class ArrayWriter implements Runnable{
private final SimpleArray sharedSimpleArray;
private final Scanner input;
public ArrayWriter(SimpleArray array, Scanner input)
{
this.input = input;
sharedSimpleArray = array;
}
@Override
public void run()
{
//add input from file here
try
{
while(input.hasNext())
{
String word = next();
//System.out.println(word);
sharedSimpleArray.add(word);
}
}
catch(NoSuchElementException elementException)
{
System.err.println("File improperly formed. Terminating...");
System.exit(2);
}
catch(IllegalStateException stateException)
{
System.err.println("Error reading from file. Terminating...");
System.exit(2);
}
}
private synchronized String next()
{
return input.next();
}
}
平行类
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
public class Parallel
{
private Scanner input;
private ArrayWriter writer1;
private ArrayWriter writer2;
SimpleArray sharedSimpleArray;
public Parallel()
{
// Open file for reading
openFile();
// Construct the shared object
sharedSimpleArray = new SimpleArray(26);
// Create two tasks to write to the shared simpleArray
writer1 = new ArrayWriter(sharedSimpleArray, input);
writer2 = new ArrayWriter(sharedSimpleArray, input);
// Execute the task with an executor service
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(writer1);
executorService.execute(writer2);
executorService.shutdown();
try
{
// Wait one minute for both writers to finish waiting
boolean taskEnded = executorService.awaitTermination(1, TimeUnit.MINUTES);
if(taskEnded)
{
System.out.printf("%nContents of SimpleArray %n");
System.out.println(sharedSimpleArray);
closeFile();
}
else
{
System.out.println("Timed out waiting for the threads to finish.");
closeFile();
}
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
// open file Dictionary.txt
private void openFile()
{
try
{
input = new Scanner(Paths.get("Dictionary.txt"));
}
catch(IOException ioException)
{
System.err.println("Error Opening file. Terminating...");
System.exit(1);
}
}
// close file and terminate application
private void closeFile()
{
if(input != null)
{
input.close();
}
}
public static void main(String[] args)
{
Parallel any = new Parallel();
}
}
下面是程序的输出。
Exception in thread "pool-1-thread-1" java.nio.BufferOverflowException
at java.base/java.nio.HeapCharBuffer.put(HeapCharBuffer.java:229)
at java.base/java.io.Reader.read(Reader.java:106)
at java.base/java.util.Scanner.readInput(Scanner.java:882)
at java.base/java.util.Scanner.hasNext(Scanner.java:1446)
at ArrayWriter.run(ArrayWriter.java:24)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1135)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:844)
Contents of SimpleArray
[295536, 63853, 152946, 113156, 376411, 39234, 82616, 92359, 312954, 5455, 26809, 194887, 105192, 251370, 251561, 113653, 5883, 246109, 250242, 230862, 131483, 33070, 22405, 10492, 70574, 14758]
答案有点离题。
答案 0 :(得分:0)
我认为这是程序中可能发生的典型竞争情况。您正在尝试使用同一台扫描器读取两个线程,并且在这种逻辑下出现竞争状态-请阅读我在代码中的注释。
while(input.hasNext()) //both threads eveluate this condition as true for last character
{
String word = next(); //One thread has already advanced. So you are out of buffer
sharedSimpleArray.add(word);
}
当然,我还没有尝试运行此程序,所以不能自信地说。
但是,我宁愿建议您做更多的事情。无论您尝试做什么,我都认为文件IO操作很难并行化。磁盘容量有限,即使您放置10个线程来完成工作,您也将无法超过磁盘的最大读取速度。
使用多线程的最佳选择是使用生产者/消费者。生产者在其中读取磁盘,并使用逻辑来将其馈送到多个使用者以计算计数。
class Producer implements Runnable{
public read() {
while(input.hasNext())
{
String word = next();
//add to a Q;
}
}
}
//Multiple threads of consumer.
Class Consumer implements Runnable{
void consume() {
//process Q.peek()
}
}
或者至少添加围绕阅读逻辑同步的内容。
while(input.hasNext())
{
String word = null;
synchronized(input) {
if(input.hasNext()) {
word = next();
}
}
if (word != null) {
sharedSimpleArray.add(word);
}
}
它有点难看,但无济于事。