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>();