我遇到了多线程应用程序的问题。我想从类似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++);
}
}
编辑 - 添加评论以指出问题出现的位置
答案 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);
}
我使用了一个列表,因为它几乎总是有用的(所以当它完成后你可以做多件事。)