java.io.File对象上的同步

时间:2012-05-07 15:05:22

标签: java multithreading file-io synchronization

java.io.File对象上使用synchronized是否合适。当您想要使用两个线程读取和写入该文件对象时:一个用于读取,一个用于写入。

public class PrintChar {
    File fileObj;
    public void read() {

    while (true) {
        synchronized (this) {
            readFile();
            notifyAll();
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName()
                        + " throws Exception");
                e.printStackTrace();
            }
        }
    }
}

public void write(String temp) {

    while (true) {
        synchronized (this) {
            writeFile(temp);
            notifyAll();
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName()
                        + " throws Exception");
                e.printStackTrace();
            }
        }
    }
}

public void setFileObj(File fileObj) {
    this.fileObj = fileObj;
}

public void readFile() {
    InputStream inputStream;
    try {
        inputStream = new FileInputStream(fileObj);
        // Get the object of DataInputStream
        DataInputStream in = new DataInputStream(inputStream);
        BufferedReader br = new BufferedReader(new InputStreamReader(in));
        String strLine;
        // Read File Line By Line
        while ((strLine = br.readLine()) != null) {
            // Print the content on the console
            System.out.println(strLine);
        }
        in.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void writeFile(String temp) {
    BufferedWriter bw;
    try {
        bw = new BufferedWriter(new FileWriter(fileObj, true));
        bw.write(temp);
        bw.newLine();
        bw.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public static void main(String args[]) {

    final PrintChar p = new PrintChar();
    p.setFileObj(new File("C:\\sunny.txt"));

    Thread readingThread = new Thread(new Runnable() {
        @Override
        public void run() {
            p.read();
        }
    });
    Thread writingThread = new Thread(new Runnable() {
        @Override
        public void run() {
            p.write("hello");
        }
    });

    Thread Randomizer = new Thread(new Runnable() {
        @Override
        public void run() {
            while (true)
                try {
                    Thread.sleep(500000);
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName()
                            + " throws Exception");
                    e.printStackTrace();
                }
        }
    });

    readingThread.start();
    writingThread.start();
    Randomizer.start();
}

}

在上面的代码中我使用了Synchronized(this),我可以使用Synchronize(fileObj)??

我从我的一位教授那里得到的另一个解决方案是将读取和写入封装在每个操作后将它们推入FIFO中,如果有人详细说明的话

4 个答案:

答案 0 :(得分:3)

修改

现在您已添加了代码,可以锁定fileObj,但前提是它未被更改。我会将其移至构造函数并使其final以确保有人不会不恰当地调用setFileObj。如果this.fileObj不是null,则抛出或抛出异常。

结合其他评论:

  • 除非确实需要通知多个线程,否则不要使用notifyAll()
  • 如果你抓住InterruptedException,我会退出线程而不是循环。始终做好关于抓取InterruptedException只是打印和循环的决定。
  • 您的in.close();应该在finally区块内。

只要两个线程都锁定在同一个常量对象上,就可以锁定所需的任何对象。通常使用private final对象,例如:

  private final File sharedFile = new File(...);

  // reader
  synchronized (sharedFile) {
       // read from file
  }
  ...

  // writer
  synchronized (sharedFile) {
       // write to file
  }

无法做的事情是锁定两个不同的File对象,即使它们都指向同一个文件。以下的工作原理如下:

  private static final String SHARED_FILE_NAME = "/tmp/some-file";

  // reader
  File readFile = new File(SHARED_FILE_NAME);
  synchronized (readFile) {
      ...
  }

  // writer
  File writeFile = new File(SHARED_FILE_NAME);
  synchronized (writeFile) {
      ...
  }

另外,仅仅因为你锁定了同一个File对象并不意味着读写代码在线程之间起作用。您需要确保在编写器中所有更新都在synchronized块中刷新。在阅读器中,您可能不希望使用缓冲流,否则您将拥有陈旧数据。

答案 1 :(得分:2)

通常,锁定I / O并不是一个好主意。最好构建你的程序,以便你通过设计保证通常不会同时写入和读取文件的给定部分,并且只有在绝对必须在文件的给定部分的读写之间进行调解时才会锁定。

答案 2 :(得分:0)

通常不是。有更好的方法:使用ReentrantLock

这个课程已经提供了“读/写锁”的比喻。它还可以正确处理许多线程可以同时读取的情况,但只有一个线程可以写入。

正如其他人已经提到的,只有当所有线程使用相同的File实例时,锁定才会起作用。

确保在每次写入后刷新输出缓冲区;这将花费一些性能,但否则,你会得到陈旧的读取(读取线程将找不到你期望的数据)。

如果要简化代码,请添加第三个线程,该线程接受来自其他两个的命令。命令为READWRITE。将命令放入队列中,让第3个线程等待队列中的条目。每个命令都应该有一个回调方法(如success()),第三个线程在执行命令时将调用该方法。

这样,您根本不需要任何锁定。每个线程的代码将更加简单,易于测试。

[编辑] 根据您的代码回答:它适用于您的情况,因为每个人都使用相同的fileObj实例,但它会将多个内容混合到一个字段中。阅读代码的人会希望文件对象只是要读取的文件的路径。因此解决方案会违反principle of least astonishment

如果您认为它可以节省内存,那么我会回复“premature optimization”。

尝试找到明确传达您意图的解决方案。 “聪明”的编码对你的自我有好处,但这是关于它可以说的唯一积极的事情(在你第一次看到你的“聪明”代码后,你的自我了解人们对你会说些什么并不好......); - )

答案 3 :(得分:0)

将读/写对象排队到一个然后执行操作的线程是一种有效的方法,但我不确定是什么。

例如,如果你在之前的问题中指定的那样,那就不会强制执行读/写/读/写命令。没有什么可以阻止读取线程排队100个读取请求。

这可以通过使提交对象的线程等待它直到它被读/写线程发出信号来防止,但这似乎是一种非常复杂的方式来执行读/写顺序,(假设你仍然是这样)要)。

我现在到了这个州,我不确定你需要/想要什么。