简体   繁体   English

在Java对象上同步文件访问

[英]File access synchronized on Java object

I have an object responsible for persisting JTable state to disk. 我有一个对象负责将JTable状态持久化到磁盘。 It saves/loads visible columns, their size, position etc. A few interesting bits from its class definition are below. 它保存/加载可见列,它们的大小,位置等。下面是类定义中的一些有趣的部分。

class TableSaver {
    Timer timer = new Timer(true);

    TableSaver() {
        timer.schedule(new TableSaverTimerTask(), 15000, SAVE_STATE_PERIOD);
    }

    synchronized TableColumns load(PersistentTable table) {
        String xml = loadFile(table.getTableKey());
        // parse XML, return
    }

    synchronized void save(String key, TableColumns value) {
        try {
            // Some preparations
            writeFile(app.getTableConfigFileName(key), xml);
        } catch (Exception e) {
            // ... handle
        }
    }

    private class TableSaverTimerTask extends TimerTask {
        @Override
        public void run() {
            synchronized (TableSaver.this) {
                Iterator<PersistentTable> iterator = queue.iterator();
                while (iterator.hasNext()) {
                    PersistentTable table = iterator.next();
                    if (table.getTableKey() != null) {
                        save(table.getTableKey(), dumpState(table));
                    }
                    iterator.remove();
                }
            }
        }
    }
}
  • There only exists one instance of TableSaver , ever. 只有一个TableSaver实例。
  • load() can be called from many threads. load()可以从许多线程调用。 Timer clearly is another thread. 计时器显然是另一个线程。
  • loadFile() and writeFile() do not leave open file streams - they use a robust, well tested and broadly used library which always closes the streams with try ... finally . loadFile()writeFile()不会留下打开的文件流 - 它们使用一个强大的,经过良好测试和广泛使用的库,它总是通过try ... finally来关闭流。

Sometimes this fails with an exception like: 有时这会失败,例外情况如下:

java.lang.RuntimeException: java.io.FileNotFoundException: C:\path\to\table-MyTable.xml (The requested operation cannot be performed on a file with a user-mapped section open)
    at package.FileUtil.writeFile(FileUtil.java:33)
    at package.TableSaver.save(TableSaver.java:175)
    at package.TableSaver.access$600(TableSaver.java:34)
    at package.TableSaver$TableSaverTimerTask.run(TableSaver.java:246)
    at java.util.TimerThread.mainLoop(Unknown Source)
    at java.util.TimerThread.run(Unknown Source)
Caused by: java.io.FileNotFoundException: C:\path\to\table-MyTable.xml (The requested operation cannot be performed on a file with a user-mapped section open)
    at java.io.FileOutputStream.open(Native Method)
    at java.io.FileOutputStream.<init>(Unknown Source)
    at java.io.FileOutputStream.<init>(Unknown Source)
    at package.FileUtilWorker.writeFile(FileUtilWorker.java:57)
    ... 6 more

So I have two questions: 所以我有两个问题:

  1. How can this kind of synchronization fail? 这种同步怎么会失败? Note that I am sure there only is one instance of TableSaver . 请注意,我确信只有一个TableSaver实例。
  2. What is this thing in the stacktrace: package.TableSaver.access$600(TableSaver.java:34) ? stacktrace中的这个东西是什么: package.TableSaver.access$600(TableSaver.java:34) Line 34 is the line with class TableSaver { . 第34行是class TableSaver {的行。 Can this be the reason why the synchronization is not working? 这可能是同步不起作用的原因吗?

Google learns me that this seems to be Windows specific. 谷歌告诉我这似乎是Windows特定的。 Here's an extract of Bug 6354433 : 这是Bug 6354433的摘录

This is Windows platform issue with memory-mapped file, ie MappedByteBuffer . 这是内存映射文件的Windows平台问题,即MappedByteBuffer The Java 5.0 doc for FileChannel state that "the buffer and the mapping that it represents will remain valid until the buffer itself is garbage-collected". FileChannel的Java 5.0 doc声明“缓冲区及其表示的映射将保持有效,直到缓冲区本身被垃圾收集”。 The error occurs when we tried to re-open the filestore and the mapped byte buffer has not been GC. 当我们尝试重新打开文件存储并且映射的字节缓冲区不是GC时,会发生错误。 Since there is no unmap() method for mapped byte buffer (see bug 4724038), we're at the mercy of the underlying operating system on when the buffer get free up. 由于映射字节缓冲区没有unmap()方法(参见bug 4724038),因此当缓冲区释放时,我们将受到底层操作系统的支配。 Calling System.gc() might free up the buffer but it is not guarantee. 调用System.gc()可能会释放缓冲区但不保证。 The problem doesn't occurs on Solaris; Solaris上不会出现此问题; may be due to the way shared memory is implemented on Solaris. 可能是由于Solaris上实现共享内存的方式。 So the work-around for Windows is not to use memory-mapped file for the transaction information tables. 因此,Windows的解决方法是不要将内存映射文件用于事务信息表。

What Java/Windows version are you using? 您使用的是什么Java / Windows版本? Does it have the latest updates? 它有最新的更新吗?

Here are two other related bugs with some useful insights: 以下是另外两个相关的错误,其中包含一些有用的见解:


As to your second question, that's just the autogenerated classname of an inner or anonymous class. 至于你的第二个问题,那只是内部或匿名类的自动生成的类名。

Assuming there are no issues with the code I have seen this occur when a virus scanner is running in the background which is cheerfully opening files to scan them behind the scenes. 假设我看到的代码没有问题,当病毒扫描程序在后台运行时会发生这种情况,这会快速打开文件在后台扫描它们。 If you have a memory resident virus scanner that checks files in the background try disabling it, or at least disabling it for the directory you reading from/writing to. 如果您有一个内存驻留病毒扫描程序,它在后台检查文件,请尝试禁用它,或者至少禁用它来读取/写入目录。

Your code looks fine. 你的代码看起来很好。 Are you sure it's not related to file permission? 您确定它与文件权限无关吗? Does the application has write privilege to this folder? 应用程序是否具有此文件夹的写权限? To this file? 到这个文件?


[EDIT] This seems to be Windows related, not Java The requested operation cannot be performed on a file with a user-mapped section open. [编辑]这似乎与Windows相关,而不是Java 无法在打开用户映射部分的文件上执行所请求的操作。

I had this issue with some tightly threaded Java code. 我遇到了一些带有严格线程的Java代码的问题。 I took a look at the referenced .NET conversation and the penny dropped. 我看了一下引用的.NET对话,下了便士。 It is simply that I have contention for the same file, among different threads. 简单地说,我在不同的线程中争用同一个文件。 Looking more closely the contention is ( also ) for some internals too. 对于一些内部人员来说,更密切地看待争论也是如此。 So my best course is to synchronize-d around the shared object when the update it. 所以我最好的方法是在更新共享对象时同步-d

This works and the error dissolves in the mist. 这有效,错误在雾中消失。

    private static   ShortLog   tasksLog     = new ShortLog( "filename" );
    private static   Boolean    tasksLogLock = false;

      ...

    synchronized( tasksLogLock ){
        tasksLog.saveLastDatum( this.toString() );
    }

see also : 另见

Your synchronization only protects against access from your own process. 您的同步仅防止来自您自己的进程的访问。 If you want to protect against accesses from any process, you have to use file locking: 如果要防止来自任何进程的访问,则必须使用文件锁定:

http://download.oracle.com/javase/1.4.2/docs/api/java/nio/channels/FileLock.html http://download.oracle.com/javase/1.4.2/docs/api/java/nio/channels/FileLock.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM