读取具有两个线程的文件时引发java.nio.BufferOverflowException

时间:2018-10-07 15:49:02

标签: java multithreading

我的任务是使用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]

答案有点离题。

1 个答案:

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

它有点难看,但无济于事。