【问题标题】:Java: opening and reading from a file without locking itJava:打开和读取文件而不锁定它
【发布时间】:2011-02-01 23:57:48
【问题描述】:

我需要能够用 Java 模仿“tail -f”。我正在尝试读取一个日志文件,因为它正在由另一个进程写入,但是当我打开文件来读取它时,它会锁定文件并且其他进程无法再写入它。任何帮助将不胜感激!

这是我目前使用的代码:

public void read(){
    Scanner fp = null;
    try{
        fp = new Scanner(new FileReader(this.filename));
        fp.useDelimiter("\n");
    }catch(java.io.FileNotFoundException e){
        System.out.println("java.io.FileNotFoundException e");
    }
    while(true){
        if(fp.hasNext()){
            this.parse(fp.next());
        }           
    }       
}

【问题讨论】:

  • 如果没有其他方法,您可以使用 jni 来访问 win32 api(假设您在 windows 上,否则无论您使用什么 api)。
  • 使用 win32 api 将是最丑陋的事情 ;)
  • 嘿,因为我不太了解 Java,这是我最好的建议 :)
  • 我选择 Java 是为了实现可移植性,所以这不是一个选项。尽管 Cshah 在我回家时提到过,但我将研究 FileChannel API。感谢您的帮助
  • JNI 建议太离谱了。正是 Win32 API 在此处执行锁定。 Java 不这样做。使用 FileChannel 也无济于事。我会审查整个要求。日志文件不是要解析的,它们是供人类使用的。如果您需要从应用程序的另一部分进行通信,请使用 Socket 或数据库。

标签: java filelock java-io


【解决方案1】:

java.io 给你一个强制文件锁,java.nio 给你一个 咨询文件锁定

如果你想在没有任何锁的情况下读取任何文件,你可以使用下面的类

import java.nio.channels.FileChannel;
import java.nio.file.Paths;

如果您想逐行跟踪文件,请使用下面的代码

public void tail(String logPath){
    String logStr = null;
    FileChannel fc = null;
    try {
        fc = FileChannel.open(Paths.get(logPath), StandardOpenOption.READ);
        fc.position(fc.size());
    } catch (FileNotFoundException e1) {
        System.out.println("FileNotFoundException occurred in Thread : " + Thread.currentThread().getName());
        return;
    } catch (IOException e) {
        System.out.println("IOException occurred while opening FileChannel in Thread : " + Thread.currentThread().getName());
    }
    while (true) {
        try {
            logStr = readLine(fc);
            if (logStr != null) {
                System.out.println(logStr);
            } else {
                Thread.sleep(1000);
            }
        } catch (IOException|InterruptedException e) {
            System.out.println("Exception occurred in Thread : " + Thread.currentThread().getName());
            try {
                fc.close();
            } catch (IOException e1) {
            }
            break;
        }
    }
}

private String readLine(FileChannel fc) throws IOException {
    ByteBuffer buffers = ByteBuffer.allocate(128);
    // Standard size of a line assumed to be 128 bytes
    long lastPos = fc.position();
    if (fc.read(buffers) > 0) {
        byte[] data = buffers.array();
        boolean foundTmpTerminator = false;
        boolean foundTerminator = false;
        long endPosition = 0;
        for (byte nextByte : data) {
            endPosition++;
            switch (nextByte) {
            case -1:
                foundTerminator = true;
                break;
            case (byte) '\r':
                foundTmpTerminator = true;
                break;
            case (byte) '\n':
                foundTmpTerminator = true;
                break;
            default:
                if (foundTmpTerminator) {
                    endPosition--;
                    foundTerminator = true;
                }
            }
            if (foundTerminator) {
                break;
            }
        }
        fc.position(lastPos + endPosition);
        if (foundTerminator) {
            return new String(data, 0, (int) endPosition);
        } else {
            return new String(data, 0, (int) endPosition) + readLine(fc);
        }
    }
    return null;
}

【讨论】:

    【解决方案2】:

    由于文件截断和(中间)删除等一些特殊情况,重建尾部很棘手。要在不锁定的情况下打开文件,请使用 StandardOpenOption.READ 和新的 Java 文件 API,如下所示:

    try (InputStream is = Files.newInputStream(path, StandardOpenOption.READ)) {
        InputStreamReader reader = new InputStreamReader(is, fileEncoding);
        BufferedReader lineReader = new BufferedReader(reader);
        // Process all lines.
        String line;
        while ((line = lineReader.readLine()) != null) {
            // Line content content is in variable line.
        }
    }
    

    关于我在 Java 中创建尾部的尝试,请参阅:

    您可以随意从该代码中获取灵感,或者直接复制您需要的部分。如果您发现任何我不知道的问题,请告诉我。

    【讨论】:

      【解决方案3】:

      Windows 对文件使用强制锁定,除非您在打开时指定正确的共享标志。如果你想打开一个繁忙的文件,你需要 Win32-API CreateFile 一个带有共享标志FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE 的句柄。

      这在 JDK 内部的一些地方用于打开文件以读取属性和内容,但据我所知,它不能导出/可用于 Java 类库级别。所以你需要找到一个本地库来做到这一点。

      我认为作为一种快速解决方法,您可以从命令 "cmd /D/C type file.lck" 中读取 process.getInputStream()

      【讨论】:

      • 我不确定它何时更改,但我使用 RandomAccessFile 打开的文件以 SHARE_READ 和 SHARE_WRITE 打开。我使用 ProcMon.exe 进行了测试
      【解决方案4】:

      查看 FileChannel API here。要锁定文件,您可以查看here

      【讨论】:

      • 你可以在stackoverflow.com/questions/128038/…查看stackoverflow中可能的重复问题
      • 嗯。我一直在查看它并尝试了几种策略——即使在 FileChannel 之外使用 java.io.RandomAccessFile,但我仍然无法让另一个进程写入我正在用 Java 读取的文件。跨度>
      • rogue780 - 发布您尝试过的策略。请注意,如果另一个进程在写入文件之前需要一个独占锁,那么您就很高兴了(当然,tail 在这种情况下也不起作用)。但是您是否尝试过锁定(使用非排他性读锁)文件的顶部?
      • 锁的行为也特定于底层操作系统。我没有尝试过共享锁,但我想你可以在 linux 中尝试。我怀疑 Windows 是否会支持它
      猜你喜欢
      • 1970-01-01
      • 2021-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多