在Java对象上同步文件访问

Kon*_*rus 10 java multithreading

我有一个对象负责将JTable状态持久化到磁盘.它保存/加载可见列,它们的大小,位置等.下面是类定义中的一些有趣的部分.

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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
  • TableSaver永远只存在一个实例.
  • load()可以从许多线程调用.计时器显然是另一个线程.
  • loadFile()并且writeFile()不要留下开放文件流 - 它们使用一个强大的,经过良好测试和广泛使用的库,它总是关闭流try ... finally.

有时这会失败,例外情况如下:

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
Run Code Online (Sandbox Code Playgroud)

所以我有两个问题:

  1. 这种同步怎么会失败?请注意,我确信只有一个实例TableSaver.
  2. 这是什么东西在堆栈跟踪:package.TableSaver.access$600(TableSaver.java:34)?第34行是该行class TableSaver {.这可能是同步不起作用的原因吗?

Bal*_*usC 9

谷歌告诉我这似乎是Windows特定的.这是Bug 6354433的摘录:

这是内存映射文件的Windows平台问题,即MappedByteBuffer.Java 5.0 doc用于FileChannel声明"缓冲区及其表示的映射将保持有效,直到缓冲区本身被垃圾收集".当我们尝试重新打开文件存储并且映射的字节缓冲区不是GC时,会发生错误.由于没有unmap()映射字节缓冲区的方法(参见bug 4724038),因此当缓冲区释放时,我们将受到底层操作系统的支配.调用System.gc()可能会释放缓冲区但不保证.Solaris上不会出现此问题; 可能是由于Solaris上实现共享内存的方式.因此,Windows的解决方法是不要将内存映射文件用于事务信息表.

您使用的是什么Java/Windows版本?它有最新的更新吗?

以下是另外两个相关的错误,其中包含一些有用的见解:


至于你的第二个问题,那只是内部或匿名类的自动生成的类名.