我有一个对象负责将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();
}
}
}
}
}
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
所以我有两个问题:
TableSaver
。package.TableSaver.access$600(TableSaver.java:34)
?第34行是class TableSaver {
的行。这可能是同步不起作用的原因吗?答案 0 :(得分:9)
Google了解到这似乎是Windows特有的。以下是Bug 6354433的摘录:
这是内存映射文件的Windows平台问题,即
MappedByteBuffer
。用于FileChannel
的Java 5.0 doc声明“它表示的缓冲区和映射在缓冲区本身被垃圾收集之前将保持有效”。当我们尝试重新打开文件存储并且映射的字节缓冲区不是GC时,会发生错误。由于映射字节缓冲区没有unmap()
方法(参见bug 4724038),因此当缓冲区释放时,我们将受到底层操作系统的支配。调用System.gc()
可能会释放缓冲区但不保证。 Solaris上不会出现此问题;可能是由于Solaris上实现共享内存的方式。因此,Windows的解决方法是不使用内存映射文件作为事务信息表。
您使用的是什么Java / Windows版本?它有最新的更新吗?
以下是另外两个相关的错误,其中包含一些有用的见解:
关于你的第二个问题,那只是内部或匿名类的自动生成的类名。
答案 1 :(得分:2)
假设我看到的代码没有问题,当病毒扫描程序在后台运行时会发生这种情况,这会快速打开文件在后台扫描它们。如果您有一个内存驻留病毒扫描程序,它在后台检查文件,请尝试禁用它,或者至少禁用它来读取/写入目录。
答案 2 :(得分:1)
您的代码看起来很好。您确定它与文件权限无关吗?应用程序是否具有此文件夹的写权限?到这个文件?
[编辑]这似乎与Windows有关,而不是Java The requested operation cannot be performed on a file with a user-mapped section open.
答案 3 :(得分:0)
您的同步仅防止来自您自己的进程的访问。如果要防止来自任何进程的访问,则必须使用文件锁定:
http://download.oracle.com/javase/1.4.2/docs/api/java/nio/channels/FileLock.html
答案 4 :(得分:0)
我遇到了一些紧密线程的Java代码。我看了一下引用的.NET对话,下了便士。简单地说,我在不同的线程中争用同一个文件。对于一些内部人员而言,更密切关注的是(也)。因此,我最好的做法是在更新共享对象时 synchronize-d 。
这有效,错误在雾中消失。
private static ShortLog tasksLog = new ShortLog( "filename" );
private static Boolean tasksLogLock = false;
...
synchronized( tasksLogLock ){
tasksLog.saveLastDatum( this.toString() );
}
另见: