字段超出范围java

时间:2015-08-10 10:52:54

标签: java multithreading arraylist

我遇到了多线程应用程序的问题。我想从类似CSV的文件中加载数据并将其存储在缓冲区中以便以后检索。

似乎动物列表在线程完成时超出范围(?),有一些关于java线程的东西我不理解并且会感激帮助。

它被调用的方式:

ParseCSV parser = new ParseCSV(null);
EventQueue.invokeLater(parser);
System.err.println("printing!");

编辑 - 根据要求,这是失败的部分 - 此时praser缓冲区的内容为空。我认为如果我在解析器中使用new关键字,动物的内容将是持久的。

while(parser.hasNext()){
    System.err.println("#");
    System.err.println(parser.getNext().toString());
}

解析器类:

public class ParseCSV implements Runnable{

    private String path = null;
    private File file = null;
    private static Logger log = LogManager.getLogger(ParseCSV.class.getName());
    private volatile ArrayList<Animal> animals = new ArrayList<Animal>();
    private int pointer =0;



    public ParseCSV(String path) {
        animals = new ArrayList<Animal>(); //tried to reinitialize, didn't help this is where the problem occurs
        if(path == null){
            JFileChooser jfc = new JFileChooser();
            jfc.showOpenDialog(null);
            file = jfc.getSelectedFile();
            this.path = file.getAbsolutePath();
        }
        else {
            this.path = path;

        }
        log.debug("Initialized file parser for " + this.path);
    }

    @Override
    public void run() {
        log.debug("begining file parse");
        System.err.println("runnner");
        try {
            Scanner fileScan = new Scanner(this.file);
            while(fileScan.hasNextLine()){
                parseLine(fileScan.nextLine());
            }
            fileScan.close();
        } catch (FileNotFoundException e) {
            log.error("Exception occured: " + e.getMessage() + "\nstack:\n" + e.getStackTrace());
            System.err.println("Exception: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private void parseLine(String nextLine) {
        Scanner lineScanner = new Scanner(nextLine);
        lineScanner.useDelimiter("\\s\\|\\s");
        if(lineScanner.hasNext()){
            Animal an =             new Animal(lineScanner.next(), //person
                    lineScanner.next(), //animal
                    Integer.parseInt(lineScanner.next()), //emp-number
                    lineScanner.next(), //date
                    Integer.parseInt(lineScanner.next()), //ani-number
                    lineScanner.next());
            animals.add(an);    //contract ID
            System.err.println(an.toString()); //prints correct result!
        }
        lineScanner.close();
    }

    public String getPath() {       return path;    }
    public void setPath(String path) {      this.path = path;   }

    public boolean hasNext(){
        System.err.println("size of data = " + animals.size());
        if(animals.size() == pointer || animals.isEmpty()) return false;
        else return true;
    }
    public Sale getNext(){
        if(animals.size() == pointer) return null;
        return animals.get(pointer++);

    }

}

编辑 - 添加评论以指出问题出现的位置

3 个答案:

答案 0 :(得分:2)

您需要等待Parser线程完成(当前两者都在同一时间发生)这可能是您的程序没有打印任何内容的原因。 您可以尝试添加一些日志记录语句,并检查序列是否正确。

答案 1 :(得分:1)

EventQueue#invokeLater向事件队列添加任务,因此在进入下一行时可能无法完成。 请改用EventQueue#invokeAndWait,等待任务完成。

答案 2 :(得分:0)

您的代码存在一些问题。其中一些实际上导致了问题,有些只是表明你并不真正理解你想要做什么。

private volatile ArrayList<Animal> animals = new ArrayList<Animal>();

你为什么要使用volatile呢?每个线程都有自己的动物列表,因此它们只被一个线程使用。如果您的主类有一个每个人都添加的动物模型,那么您需要使其变得易变,因为多个线程可以访问它。

    animals = new ArrayList<Animal>(); //tried to reinitialize, didn't help this is where the problem occurs

这不会改变任何事情。如果您认为该方法可以被多次调用并且每次都想要一个新的列表,那么它可能是有用的,但它可能只是浪费。

现在谈到真正的问题:

当你调用一个线程时,因为你希望它在后台运行而不是停止主程序的执行。然后,您可以在实际结束时的某个时间返回结果。正如人们所说的那样,如果你只是想要一个没有它真正在后台运行的新线程你可以做EventQueue#invokeAndWait,但对于你的情况,这不是解决方案。

您可以使用观察者模式。要做到这一点,你创建一个具有notify方法的接口(称之为parseListner或其他东西)。解析器有一个parseListerners列表。当解析器完成解析时,它会告诉所有侦听器(通过调用接口上的方法)。所以你最终得到的代码如下:

public interface ParseListner{
   void fileParsed(String fileName, List<Animal> animals);
}

public class Main implements ParseListner{
   public void main(){
      ParseCSV parser = new ParseCSV(this);
      EventQueue.invokeLater(filePath, parser);
   }
   public void fileParsed(String fileName, List<Animal> animals){
      System.Out.Println(doneParsing);
   }
}

public class ParseCSV实现Runnable {     List listners = new ArrayList&lt;&gt;();     public ParseCSV(String path,ParseListner caller){     listner.add(主叫)     }

@Override
public void run() {
    //all the parsestuff
    for(ParseListner p : listners)
       p.parsedFile(fileName, animals);
}

我使用了一个列表,因为它几乎总是有用的(所以当它完成后你可以做多件事。)