我正在处理一个从.csv文件中读取项目的程序,并将它们写入远程数据库。我正在尝试多线程程序,为此我创建了2个具有不同连接的进程线程。为此,将.csv文件读入缓冲读取器,并处理缓冲读取器的内容。但是,似乎线程不断复制数据(将每个元组的两个副本写入数据库)。
我一直在试图弄清楚如何在Java中使用mutex缓冲区,而我最接近的就是优先级队列。
我的问题是,您是否可以使用缓冲读卡器逐行将文件读入优先级队列?即。
public void readFile(Connection connection) {
BufferedReader bufReader = null;
try{
bufReader = new BufferedReader(new FileReader(RECS_FILE));
bufReader.readLine(); //skip header line
String line;
while((line = bufReader.readLine()) != null) {
//extract fields from each line of the RECS_FILE
Pattern pattern = Pattern.compile( "\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\"");
Matcher matcher = pattern.matcher(line);
if(!matcher.matches()) {
System.err.println("Unexpected line in "+RECS_FILE+": \""+line+"\"");
continue;
}
String stockSymbol = matcher.group(1);
String recDateStr = matcher.group(2);
String direction = matcher.group(3);
String completeUrl = matcher.group(4);
//create recommendation object to populate required fields
// and insert it into the database
System.out.println("Inserting to DB!");
Recommendation rec = new Recommendation(stockSymbol, recDate, direction, completeUrl);
rec.insertToDb(connection);
}
} catch (IOException e) {
System.err.println("Unable to read "+RECS_FILE);
e.printStackTrace();
} finally {
if(bufReader != null) {
try{
bufReader.close();
} catch (IOException e) {
}
}
}
}
您将看到使用缓冲读取器读取.csv文件。有没有办法在函数外部设置优先级队列,以便缓冲的读取器将元组放入优先级队列,然后每个程序线程访问优先级队列?
答案 0 :(得分:1)
缓冲读者,或者实际上任何读者或流本质上仅用于单线程使用。优先级队列是一个完全独立的结构,根据实际的实现,可能会或可能不会被多个线程使用。所以简短的回答是:不,它们是两个完全不相关的概念。
解决原始问题:您无法使用多线程的流式文件访问。您理论上可以使用RandomAccessFile
,但您的线条不是固定宽度,因此在不读取文件中的所有内容之前,您不能seek()
到行的开头。此外,即使您的数据由固定的记录组成,读取具有两个不同线程的文件也可能是不切实际的。
唯一可以并行化的是数据库插入,明显需要注意的是丢失了事务性,因为必须为每个线程使用单独的事务。 (如果不这样做,则必须同步数据库操作,这再次意味着您没有赢得任何东西。)
因此,解决方案可以是从一个线程读取行并将字符串传递给通过ExecutorService
调用的处理方法。这样可以很好地扩展,但是有一点需要注意:增加数据库锁定的开销可能会使使用多个线程的优势无效。
最终的教训可能不是过于复杂化:尝试简单的方法,只有在简单的方法不起作用时才寻找更复杂的解决方案。另一个教训可能是多线程不能帮助I / O绑定程序。
答案 1 :(得分:0)
@ Biziclop的回答是(+1),但我想我会添加一些关于批量数据库插入的内容。
如果您不知道,在大多数SQL数据库中关闭数据库自动提交是批量插入过程中的一大胜利。通常在每个SQL语句之后,数据库将其提交到磁盘存储,磁盘存储更新索引并对磁盘结构进行所有更改。通过关闭此自动提交,数据库只需在最后调用commit
时进行这些更改。通常你会做类似的事情:
conn.setAutoCommit(false);
for (Recommendation rec : toBeInsertedList) {
rec.insertToDb(connection);
}
conn.setAutoCommit(true);
此外,如果数据库不支持自动提交,通常在事务中包装插入也会完成同样的事情。
以下是另一些可能有用的答案: