Как реализовать многозадачные блокировки чтения / записи (ConcurrentHashmap) - PullRequest
0 голосов
/ 04 июля 2018

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

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

Вот примерный код.

private static final ConcurrentMap<String, String> lockMap = new ConcurrentHashMap();


private void createCache(String templatePath, String cachePath){

//get template 
String temp = getTemplate(templatePath);

String myRand = randomString();
lockMap.put(cachePath,myRand);

// save cache file
  try {
    // ** is  lockMap.get(cachePath) still threadsafe if another thread has changed the row's value?
    synchronized ( lockMap.get(cachePath) ){
      Files.write(Paths.get(cachePath),temp.getBytes(StandardCharsets.UTF_8));
    }
  } finally {
    // remove lock if not locked by another thread in the meantime
    lockMap.remove(cachePath, myRand);
  }

}


private String getCache(String cachePath){

 String output = null;

  //only lock if this specific file is being written at the moment
  if ( lockMap.contains(cachePath) ){
        synchronized ( lockMap.get(cachePath) ){
            output = getFile(cachePath);
        }
    } else {
        output = getFile(cachePath);
    }

  return output;

}

// main event        
private String cacheToString (String templatePath, String cachePath){

  File cache = new File(cachePath);

  if ( !cache.exists() ){
    createCache(templatePath, cachePath)
  }

  return getCache(cachePath);

}

Проблема, с которой я столкнулся, заключается в том, что хотя поток снимет блокировку запрошенного файла только в том случае, если он не был изменен другим потоком, другой поток все же может обновить значение в lockMap для этой записи - если это произойдет, сбой синхронизации?

1 Ответ

0 голосов
/ 04 июля 2018

Я бы каждый раз писал новый временный файл и переименовывал его, когда закончил. Переименование атомное.

// a unique counter across restarts
final AtomicLong counter = new AtomicLong(System.currentTimeMillis()*1000);

private void createCache(String templatePath, String cachePath) {
    //get template
    String temp = getTemplate(templatePath);

    Path path = Paths.get(cachePath);
    Path tmpPath = Paths.get(path.getParent().toString(), counter.getAndIncrement() + ".tmp");
    // save cache file
    Files.write(tmpPath, temp.getBytes(StandardCharsets.UTF_8));
    Files.move(tmpPath, path, ATOMIC_MOVE, REPLACE_EXISTING);
}

Если несколько потоков пытаются записать в один и тот же файл, побеждает последний, выполнивший move.

...