我是信号量的新手,我有任何疑问。我有一个线程,它从文本文件A开始并读取行,并将它们写入其他文本文件B.我编写了这段代码,但我不确定线程是否阻塞关键部分并正确同步。因为和其他线程可以使用这些文件操作。
public static void main(String[] args) {
Thread thread = new Thread(new ThreadManager());
thread.start();
}
线程类:
public class ThreadManager extends Thread {
private Semaphore semaphore;
public ThreadManager() {
this.semaphore = new Semaphore(1);
}
public void run() {
try {
this.semaphore.acquire();
BufferedReader br = null;
String line;
String fileNme = "threadLog.txt";
ArrayList<String> fileLines = new ArrayList<String>();
int numLine = 0;
File outFile = new File("$$$$$$$$.tmp");
// input
FileInputStream fis = null;
PrintWriter out = null;
try {
fis = new FileInputStream(fileNme);
// output
FileOutputStream fos = new FileOutputStream(outFile);
out = new PrintWriter(fos);
} catch (FileNotFoundException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
try {
while ((line = in.readLine()) != null) {
fileLines.add(line);
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (!fileLines.isEmpty()) {
int middleLine = (int) Math.round(fileLines.size() / 2);
fileLines.add(middleLine, Thread.currentThread().getName());
for (int i = 0; i < fileLines.size(); i++) {
out.println(fileLines.get(i));
}
out.flush();
out.close();
try {
in.close();
new File(fileNme).delete();
outFile.renameTo(new File(fileNme));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.semaphore.release();
} catch (InterruptedException e3) {
// TODO Auto-generated catch block
e3.printStackTrace();
}
}
答案 0 :(得分:1)
使用信号量或同步块(或其他),您无法保证仅使用文件名同步访问文件。任何其他线程(或进程)仍然可以打开,读取或修改该文件,例如通过创建自己的FileOutputStream
并传递相同的文件名。
虽然您可以构建代码以鼓励同步访问文件,但至少在您的过程中,您无法保证它。因此,您必须对其他进程访问您的文件的可能性做出一些假设,并定义(并记录)一些关于其他线程如何访问该文件并遵守它们的规则。
看起来你只是制作一个临时文件,所以你也可以考虑File.createTempFile()
来减少使用相同文件名的可能性;为文件名添加唯一性可能有所帮助。
虽然我绝对可以详细了解具体选项,但是您的确切用法并不清楚您的问题,如果没有更多信息,我能告诉您的最好的是同步原语无法解决用于 100%保证没有其他任何东西同时访问该文件 - 您必须考虑您的情况,并在通过代码与良好的文档提供此保护之间找到平衡,并坚持遵守规则你定义。
顺便说一句,带有1个许可证的信号量与互斥锁相同,在这种情况下,您可能会发现synchronized
blocks提供了更具吸引力的语法。
另外,请不要忘记ReadWriteLock
,这是另一种有用的工具,具体取决于您的访问模式和性能要求。
答案 1 :(得分:0)
我找到Class FileChannel,现在我正在开发此代码。你有什么看法?
public class ThreadManager extends Thread {
FileChannel f = null; // The channel to the file
FileLock lock = null; // The lock object we hold
public void run() {
try {
BufferedReader br = null;
String line;
String fileNme = "threadLog.txt";
ArrayList<String> fileLines = new ArrayList<String>();
int numLine = 0;
File outFile = new File("$$$$$$$$.tmp");
// input
FileInputStream fis = null;
PrintWriter out = null;
try {
fis = new FileInputStream(fileNme);
f = fis.getChannel();
lock = f.tryLock();
if (lock != null) {
// output
FileOutputStream fos = new FileOutputStream(outFile);
out = new PrintWriter(fos);
} catch (FileNotFoundException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
try {
while ((line = in.readLine()) != null) {
fileLines.add(line);
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if (!fileLines.isEmpty()) {
int middleLine = (int) Math.round(fileLines.size() / 2);
fileLines.add(middleLine, Thread.currentThread().getName());
for (int i = 0; i < fileLines.size(); i++) {
out.println(fileLines.get(i));
}
out.flush();
out.close();
try {
in.close();
new File(fileNme).delete();
outFile.renameTo(new File(fileNme));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
} finally {
// Always release the lock and close the file
// Closing the RandomAccessFile also closes its FileChannel.
if (lock != null && lock.isValid())
lock.release();
}