Java 这个类读取文件是线程安全的吗?
有人可以检查一下这个代码,看看它是否是线程安全的吗Java 这个类读取文件是线程安全的吗?,java,Java,有人可以检查一下这个代码,看看它是否是线程安全的吗 public class FileLinesSorter { private CountDownLatch startSignal = new CountDownLatch(1); /** * The lines */ private List<String> lines; /** * Read files from the file paths * @param filePaths the list of fil
public class FileLinesSorter {
private CountDownLatch startSignal = new CountDownLatch(1);
/**
* The lines
*/
private List<String> lines;
/**
* Read files from the file paths
* @param filePaths the list of file paths
* @throws IOException on I/O error
*/
public void readFiles(String[] filePaths) throws IOException {
lines = new ArrayList<String>();
for (String filePath : filePaths) {
File file = new File(filePath);
if (!file.exists()) {
// File does not exist. Log it.
continue;
}
List<String> fileLines = readFile(file);
lines.addAll(fileLines);
}
if (!lines.isEmpty()) {
Collections.sort(lines);
}
startSignal.countDown();
}
/**
* Read a single file
* @param file the file
* @return the file content
* @throws IOException on I/O error
*/
private List<String> readFile(File file) throws IOException {
List<String> contents = new ArrayList<String>();
BufferedReader reader = null;
FileReader fileReader = null;
try {
fileReader = new FileReader(file.getAbsolutePath());
reader = new BufferedReader(fileReader);
String line = "";
while ((line = reader.readLine()) != null) {
if (line.isEmpty()) {
continue;
}
contents.add(line);
}
} catch (FileNotFoundException e) {
throw e;
} catch (IOException e) {
throw e;
} finally {
if (fileReader != null) {
fileReader.close();
}
if (reader != null) {
reader.close();
}
}
return contents;
}
public Iterator<String> getIterator() {
try {
startSignal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return lines.iterator();
}
public static void main(String[] args) throws Exception {
String[] filePaths = {"C:\\Works\\files\\example-1 copy.txt", "C:\\Works\\files\\example-2 copy.txt"};
FileLinesSorter sorter = new FileLinesSorter();
sorter.readFiles(filePaths);
Iterator<String> lines = sorter.getIterator();
while (lines.hasNext()) {
System.out.println(lines.next());
}
}
公共类FileLinesSorter{
专用倒计时闩锁启动信号=新倒计时闩锁(1);
/**
*台词
*/
私有列表行;
/**
*从文件路径读取文件
*@param filepath文件路径列表
*@在I/O错误时引发IOException
*/
public void readFiles(字符串[]文件路径)引发IOException{
行=新的ArrayList();
for(字符串文件路径:文件路径){
文件文件=新文件(文件路径);
如果(!file.exists()){
//文件不存在。请记录它。
继续;
}
列表文件行=读取文件(文件);
lines.addAll(文件行);
}
如果(!lines.isEmpty()){
集合。排序(行);
}
开始信号倒计时();
}
/**
*读取单个文件
*@param file文件
*@返回文件内容
*@在I/O错误时引发IOException
*/
私有列表读取文件(文件文件)引发IOException{
列表内容=新建ArrayList();
BufferedReader reader=null;
FileReader FileReader=null;
试一试{
fileReader=新的fileReader(file.getAbsolutePath());
reader=新的BufferedReader(文件读取器);
字符串行=”;
而((line=reader.readLine())!=null){
if(line.isEmpty()){
继续;
}
内容。添加(行);
}
}catch(filenotfounde异常){
投掷e;
}捕获(IOE异常){
投掷e;
}最后{
if(fileReader!=null){
fileReader.close();
}
if(读卡器!=null){
reader.close();
}
}
返回内容;
}
公共迭代器getIterator(){
试一试{
开始信号。等待();
}捕捉(中断异常e){
e、 printStackTrace();
}
返回行。迭代器();
}
公共静态void main(字符串[]args)引发异常{
字符串[]文件路径={“C:\\Works\\files\\example-1copy.txt”,“C:\\Works\\files\\example-2copy.txt”};
FileLinesSorter sorter=新的FileLinesSorter();
sorter.readFiles(文件路径);
迭代器行=sorter.getIterator();
while(lines.hasNext()){
System.out.println(lines.next());
}
}
}即使文件读取本身是线程安全的,读取的数据也不是线程安全的
您的
iterator()
方法是公共的,当一个线程正在读取文件时,另一个线程可以调用iterator()
方法并获取ConcurrentModificationException。(因为ArrayList是fail fast fast fail fast)。如上所述,读取的代码本身是线程安全的,但迭代器方法不应该是公共的问题在于,您需要实现Iterable接口
我认为这是一个糟糕的设计
您不应该在这里实现Iterable,而是应该有一个返回字符串对象Iterable的方法,该方法只有在您完成文件读取后才会返回Iterable对象
通过这种方式,您可以读取文件,并为其内容提供一个iterable
我建议如下:
A.有一个名为MyFileReader的类
B.在上面定义的字段中保留行。 C.有一个h对象-称这个字段为lock。让它用值1初始化
D.读取文件后,readFiles方法应执行倒计时
E.定义一个getIterator方法,该方法将为lines字段创建一个迭代器,但在创建之前,它将调用lock.await()-这意味着该方法将等待文件完全读取
我希望它更清楚。我真的应该理解如何嵌入代码。我还不明白:(在我看来,这不是线程安全的。我对线程安全的理解是,两个线程可以在同一时间调用该类的同一实例上的方法,该类将按预期的方式运行。对于该类来说,这不是真的 考虑这一点: 线程1调用
readFiles()
,这导致调用此行:
lines = new ArrayList<String>();
现在,您已经用第二个变量覆盖了第一个变量
第一个线程将调用lines.addAll(fileLines)
,它实际上将使用第二个lines
变量。这可能不是您想要的
最简单的修复方法是将synchronized
关键字添加到方法签名中。这当然会减慢速度,因为第二次调用将等待第一次调用完成
您应该使
readFiles()
实例化它自己的列表
并返回它自己的实例。然后删除private-level-lines变量。这是否意味着客户端(调用该类的人)必须注意线程安全而不是FileLinesOrder?不一定,你可以使用并发版本的list,比如ConcurrentLinkedQueue,你可以修改它以便我更好地理解吗?我想他说的是不要实现Iterable。只要有一个方法getLines()
返回行,或者一个方法getLinesIterator()
返回行。迭代器()
,仅在文件完全读取后执行。确切地说,我是stackoverflow的新手,不知道如何嵌入代码。我将尝试修改此方法。
lines = new ArrayList<String>();