一次只能运行一个Java应用程序实例。它运行在Linux上。我需要确保一个线程不会修改文件,而另一个线程正在使用它。
我不知道要使用哪种文件锁定或同步方法。我从来没有在Java中完成文件锁定,我没有太多的Java或编程经验。
我查看了Java NIO,并且我读到“文件锁代表整个Java虚拟机。它们不适合控制同一虚拟机中多个线程对文件的访问。”我知道我需要专家帮助,因为这是生产代码,我几乎不知道我在做什么(我今天必须完成它)。
以下是我将代码(存档文件)上传到服务器的代码的简要概述。它获取要从文件上传的文件列表(称之为“listFile”) - 并且可以在此方法读取时修改listFile。我通过将listFile复制到临时文件并在此后使用该临时文件来最小化这种可能性。但我认为我需要在复制过程中锁定文件(或类似的东西)。
package myPackage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import com.example.my.FileHelper;
import com.example.my.Logger;
public class BatchUploader implements Runnable {
private int processUploads() {
File myFileToUpload;
File copyOfListFile = null;
try {
copyOfListFile = new File("/path/to/temp/workfile");
File origFile = new File("/path/to/listFile"); //"listFile" - the file that contains a list of files to upload
DataWriter.copyFile(origFile, copyOfListFile);//see code below
} catch (IOException ex) {
Logger.log(ex);
}
try {
BufferedReader input = new BufferedReader(new FileReader(copyOfListFile));
try {
while (!stopRunning && (fileToUploadName = input.readLine()) != null) {
upload(new File(fileToUploadName));
}
} finally {
input.close();
isUploading = false;
}
}
return filesUploadedCount;
}
}
以下是修改上述代码中使用的文件列表的代码:
public class DataWriter {
public void modifyListOfFilesToUpload(String uploadedFilename) {
StringBuilder content = new StringBuilder();
try {
File listOfFiles = new File("/path/to/listFile"); //file that contains a list of files to upload
if (!listOfFiles.exists()) {
//some code
}
BufferedReader input = new BufferedReader(new FileReader(listOfFiles));
try {
String line = "";
while ((line = input.readLine()) != null) {
if (!line.isEmpty() && line.endsWith(FILE_EXTENSION)) {
if (!line.contains(uploadedFilename)) {
content.append(String.format("%1$s%n", line));
} else {
//some code
}
} else {
//some code
}
}
} finally {
input.close();
}
this.write("/path/to/", "listFile", content.toString(), false, false, false);
} catch (IOException ex) {
Logger.debug("Error reading/writing uploads logfile: " + ex.getMessage());
}
}
public static void copyFile(File in, File out) throws IOException {
FileChannel inChannel = new FileInputStream(in).getChannel();
FileChannel outChannel = new FileOutputStream(out).getChannel();
try {
inChannel.transferTo(0, inChannel.size(), outChannel);
} catch (IOException e) {
throw e;
} finally {
if (inChannel != null) {
inChannel.close();
}
if (outChannel != null) {
outChannel.close();
}
}
}
private void write(String path, String fileName, String data, boolean append, boolean addNewLine, boolean doLog) {
try {
File file = FileHelper.getFile(fileName, path);
BufferedWriter bw = new BufferedWriter(new FileWriter(file, append));
bw.write(data);
if (addNewLine) {
bw.newLine();
}
bw.flush();
bw.close();
if (doLog) {
Logger.debug(String.format("Wrote %1$s%2$s", path, fileName));
}
} catch (java.lang.Exception ex) {
Logger.log(ex);
}
}
}
答案 0 :(得分:2)
我的建议采用略有不同的方法。在Linux上,文件重命名(mv)操作在本地磁盘上是原子的。一个进程没有机会看到“半写”文件。
设XXX为具有三个(或更多)数字的序列号。您可以将DataWriter附加到名为listFile-XXX.prepare的文件中,并将固定数量N的文件名写入其中。写入N个名称时,关闭该文件并将其重命名(原子,见上文)到listFile-XXX。使用下一个文件名,开始写入listFile-YYY,其中YYY = XXX + 1。
您的BatchUploader可以随时检查是否找到与模式listFile-XXX匹配的文件,打开它们,读取它们上传指定文件,关闭并删除它们。线程没有机会弄乱对方的文件。
实施提示:
好处:没有锁定(这样做会很难),没有复制,工作队列的简单概述以及文件系统中的状态。
答案 1 :(得分:1)
这是一个略有不同的建议。假设你的文件名中没有'\ n'字符(这是linux上的一个很大的假设,我知道,但是你可以让你的作者看到它),为什么不只读完整行并忽略不完整的行?通过不完整的行,我的意思是以EOF结尾而不是\ n。
的行修改:请在下面的评论中查看更多建议。