Мое приложение создает индекс, который сохраняется в тысячах файлов на диске.Я хочу получить доступ к индексным файлам с несколькими экземплярами приложения.Поэтому я использую java FileLock для блокировки файлов, которые я читаю, между различными JVM.
Если я пытаюсь получить FileLock дважды с одним и тем же приложением, я получаю OverlappingFileLockException
(именно так FileLock должен работать),Чтобы предотвратить многократное приобретение одного и того же файла в одном экземпляре, я создаю карту.Если приложение может получить семафор для определенного файла, оно также может получить FileLock.Если нет, файл в данный момент используется приложением.С ConcurrentHashMap
он работает как шарм.Я использую computeIfAbsent
, чтобы добавить Семафоры на карту, если это необходимо.
Проблема в том, что чем больше файлов я создаю, тем больше семафоров я сохраняю на карте.Запуск приложения в течение нескольких дней может привести к взрыву карты.Чтобы предотвратить это, я хочу удалить неиспользуемые записи, когда они будут освобождены.
Я не могу просто удалить семафор, как во 2-й версии функции.Если семафор поставил потоки в очередь, следующий поток в очереди получит FileLock.Новый поток не найдет семафор на карте и создаст новый, снова получая FileLock.Я должен проверить, если semaphore.hasQueuedThreads()
и удалить его, только если нет очереди потока.Но это не атомная операция.Я попытался заблокировать функции получения / выпуска для семафора с помощью одного семафора, чтобы синхронизировать обе функции (плохая практика), просто чтобы посмотреть, может ли он работать.Это закончилось тупиком.
val lockMap = ConcurrentHashMap<String, Semaphore>()
fun accessFile(fileName: String) {
acquireSemaphore(fileName: String)
acquireFileLock(fileName: String)
doSomethingWithFile(fileName: String)
releaseFileLock(fileName: String)
releaseSemaphore(fileName: String)
}
fun acquireSemaphore(fileName: String) {
(lockMap.computeIfAbsent(fileName){Semaphore(1, true)}).acquire()
}
fun releaseSemaphore(fileName: String) {
lockMap[fileName]?.release() // works, but the map keeps every semaphore
}
fun releaseSemaphore(fileName: String) {
lockMap.remove(filename)?.release() // removes the semaphore, but causes OverlappingFileLockExceptions
}
Я хочу удалить семафор с карты, когда он выпущен, и нет потоков, ожидающих семафор.Это должна быть атомарная операция, поэтому второй семафор для этого файла не создается.