Доступ к файлу синхронизирован на объекте Java

У меня есть объект, отвечающий за сохранение состояния 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 () не оставляют открытые файловые потоки - они используют надежную, хорошо протестированную и широко используемую библиотеку, которая всегда закрывает потоки с помощью попробуй ... наконец .

Иногда это терпит неудачу с исключением, например:

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

Поэтому у меня есть два вопроса:

  1. Как может произойти сбой такого рода синхронизации? Обратите внимание, что я уверен, что существует только один экземпляр TableSaver .
  2. Что это за вещь в трассировке стека: package.TableSaver.access $ 600 (TableSaver.java:34) ? Строка 34 - это строка с классом TableSaver {. Может ли это быть причиной того, что синхронизация не работает?
10
задан Konrad Garus 30 August 2010 в 17:35
поделиться

3 ответа

Google узнал, что это, похоже, специфично для Windows. Вот выдержка из Ошибка 6354433:

Это проблема платформы Windows с отображаемым в память файлом, то есть MappedByteBuffer. В документе Java 5.0 для FileChannel указано, что «буфер и сопоставление, которое он представляет, останутся действительными до тех пор, пока сам буфер не будет удален сборщиком мусора». Ошибка возникает, когда мы попытались повторно открыть хранилище файлов, а отображаемый буфер байтов не был GC. Поскольку нет метода unmap() для отображаемого буфера байтов (см. ошибку 4724038), мы находимся во власти базовой операционной системы, когда буфер освобождается. Вызов System.gc() может освободить буфер, но это не гарантируется. Проблема не возникает в Solaris; может быть связано с тем, как разделяемая память реализована в Solaris. Таким образом, обходной путь для Windows заключается в том, чтобы не использовать файл с отображением памяти для таблиц информации о транзакциях.

Какую версию Java/Windows вы используете? Есть ли у него последние обновления?

Вот две другие связанные ошибки с некоторыми полезными сведениями:

  • Ошибка 4715154 — Невозможно удалить файл с отображением памяти.
  • Ошибка 4469299 — Файлы с отображением памяти не подвергаются сборке мусора.

Что касается вашего второго вопроса, это просто автоматически сгенерированное имя класса внутреннего или анонимного класса.

9
ответ дан 4 December 2019 в 00:22
поделиться

Если предположить, что с кодом нет проблем, я видел, что это происходит, когда антивирусный сканер работает в фоновом режиме, который весело открывает файлы для сканирования их за кулисами. Если у вас есть резидентный сканер вирусов в памяти, который проверяет файлы в фоновом режиме, попробуйте отключить его или, по крайней мере, отключить его для каталога, из которого вы читаете/пишете.

2
ответ дан 4 December 2019 в 00:22
поделиться

Ваш код выглядит нормально. Вы уверены, что это не связано с правами доступа к файлам? Есть ли у приложения право записи в эту папку? В этот файл?


[EDIT] Похоже, что это связано с Windows, а не с Java. Запрошенная операция не может быть выполнена в файле с открытым разделом, отображаемым пользователем.

1
ответ дан 4 December 2019 в 00:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: