有人可以查看此代码以查看它是否是线程安全的吗?
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());
}
}
}
答案 0 :(得分:3)
在我看来,这不是线程安全的。我对线程安全的理解意味着两个线程可以同时在该类的同一个实例上调用方法,并且该类将按预期运行。这个类不适用。
考虑一下:
线程1调用readFiles()
会导致调用此行:
lines = new ArrayList<String>();
现在假设第二个线程在第一个线程完成之前也调用readFiles()
。再次调用此行:
lines = new ArrayList<String>();
您现在已使用第二个变量覆盖了第一个变量。
第一个线程将调用lines.addAll(fileLines)
实际上将使用第二个lines
变量。这可能不是你想要的。
最简单的解决方法是将synchronized
关键字添加到方法签名中。这当然会减慢速度,因为第二个调用将等待第一个调用完成。
你应该让readFiles()
实例化它自己的List
并返回它自己的实例。然后删除私有级行变量。
答案 1 :(得分:2)
如上所述,读取代码本身是线程安全的,但迭代器方法不应该是公共的。
问题是你实现了Iterable接口。
我认为这是一个糟糕的设计。
你不应该在这里实现Iterable,而是让一个方法返回一个Iterable of String对象,它只在你完成读取文件后返回一个Iterable对象。
这样,您可以具有读取文件和为其内容提供迭代的功能。
我建议如下:
A.有一个名为MyFileReader的课程
B.使用上面定义的字段来保持线条。
C.拥有一个CountdownLatc h对象 - 调用此字段锁定。将其初始化为1的值
D.一旦读取文件,您的readFiles方法应该执行countDown。
E.定义一个getIterator方法,该方法将为lines字段创建一个迭代器,但在创建之前,它将调用lock.await() - 这意味着此方法将一直等到文件被完全读取。
我希望它更清楚。我真的应该不知道如何嵌入代码。我还没有得到它:(
答案 2 :(得分:1)
即使文件读取本身是线程安全的,读取数据也不是。
你的iterator()
方法是公共的,当一个线程正在读取文件时,另一个线程可以调用iterator()
方法并获得ConcurrentModificationException。(因为ArrayList是快速失败的)。